Skip to content

Commit a4e4ffc

Browse files
authored
Check for unsupported renderer (#111)
1 parent 6da3b69 commit a4e4ffc

34 files changed

+806
-348
lines changed

plainly-aescripts/src/index.ts

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,27 +12,46 @@ import './shims';
1212
import {
1313
getInstalledFontsByFamilyNameAndStyleName,
1414
getInstalledFontsByPostScriptName,
15+
selectComp,
1516
selectLayer,
16-
unselectAllLayers,
1717
} from './utils';
18-
import { fixAllIssues, validateProject } from './validation';
18+
import { fixAllCapsIssues, fixAllIssues, validateProject } from './validation';
19+
import { fixUnsupported3DRendererIssues } from './validation/compValidators';
1920

20-
const PlainlyAE = () => ({
21-
selectFolder,
21+
const collectFunctions = {
2222
collectFiles,
23+
selectFolder,
24+
relinkFootage,
25+
};
26+
27+
const projectFunctions = {
2328
setProjectData,
2429
getProjectData,
2530
removeProjectData,
2631
getProjectPath,
2732
saveProject,
2833
getAfterEffectsVersion,
29-
relinkFootage,
3034
getInstalledFontsByPostScriptName,
3135
getInstalledFontsByFamilyNameAndStyleName,
36+
};
37+
38+
const utilsFunctions = {
39+
selectLayer,
40+
selectComp,
41+
};
42+
43+
const validateFunctions = {
3244
validateProject,
3345
fixAllIssues,
34-
unselectAllLayers,
35-
selectLayer,
46+
fixAllCapsIssues,
47+
fixUnsupported3DRendererIssues,
48+
};
49+
50+
const PlainlyAE = () => ({
51+
...collectFunctions,
52+
...projectFunctions,
53+
...utilsFunctions,
54+
...validateFunctions,
3655
});
3756

3857
if ($['com.plainlyvideos.after-effects-plugin.Panel']) {

plainly-aescripts/src/utils.ts

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ function unselectAllLayers(): void {
149149
* selectLayer('1234');
150150
*/
151151
function selectLayer(layerId: string): void {
152+
unselectAllLayers();
152153
const layer = app.project.layerByID(parseInt(layerId, 10));
153154
if (layer) {
154155
// open the comp that contains the layer, so it is visible to the user in timeline
@@ -161,6 +162,46 @@ function selectLayer(layerId: string): void {
161162
}
162163
}
163164

165+
/**
166+
* Deselects all compositions in the active project.
167+
*
168+
* Iterates through all `CompItem` instances in `app.project` and sets their `selected` property to `false`.
169+
*
170+
* @returns {void}
171+
*/
172+
function unselectedAllComps(): void {
173+
const comps = getAllComps(app.project);
174+
for (let i = 0; i < comps.length; i++) {
175+
const comp = comps[i];
176+
comp.selected = false;
177+
}
178+
}
179+
180+
/**
181+
* Selects a composition in the active project by its numeric ID.
182+
*
183+
* Uses `app.project.itemByID` after parsing the provided string to base-10 integer. If a composition
184+
* with the given ID exists, it is opened in a viewer and its `selected` property is set to `true`;
185+
* otherwise the function exits silently.
186+
*
187+
* @param {string} compId - The string representation of the composition's numeric ID (e.g. value from `CompItem.id`).
188+
* @returns {void}
189+
* @example
190+
* // Select a composition whose id is 5678
191+
* selectComp('5678');
192+
*/
193+
function selectComp(compId: string): void {
194+
unselectedAllComps();
195+
const comp = app.project.itemByID(parseInt(compId, 10));
196+
if (comp instanceof CompItem) {
197+
const viewer = comp.openInViewer();
198+
comp.selected = true;
199+
if (viewer?.active === false) {
200+
viewer.setActive();
201+
}
202+
}
203+
}
204+
164205
export {
165206
getAllComps,
166207
getFolderPath,
@@ -169,6 +210,6 @@ export {
169210
getTextLayersByComp,
170211
isWin,
171212
pathJoin,
172-
unselectAllLayers,
173213
selectLayer,
214+
selectComp,
174215
};
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import type { CompIssues, RendererTypeName } from 'plainly-types';
2+
import { ProjectIssueType } from '.';
3+
4+
enum RendererType {
5+
CLASSIC_3D = 'ADBE Advanced 3d',
6+
ADVANCED_3D = 'ADBE Calder',
7+
CINEMA_4D = 'ADBE Ernst',
8+
}
9+
10+
function getRendererName(renderer: string): RendererTypeName {
11+
if (renderer === RendererType.CLASSIC_3D) return 'Classic 3D';
12+
if (renderer === RendererType.ADVANCED_3D) return 'Advanced 3D';
13+
if (renderer === RendererType.CINEMA_4D) return 'Cinema 4D';
14+
return 'Unknown Renderer';
15+
}
16+
17+
function validateComps(comps: CompItem[]): CompIssues[] {
18+
const compIssues: CompIssues[] = [];
19+
20+
for (let i = 0; i < comps.length; i++) {
21+
const comp = comps[i];
22+
const renderer = comp.renderer;
23+
if (renderer !== RendererType.CLASSIC_3D) {
24+
compIssues.push({
25+
type: ProjectIssueType.Unsupported3DRenderer,
26+
compId: comp.id.toString(),
27+
compName: comp.name,
28+
renderer: getRendererName(renderer),
29+
});
30+
}
31+
}
32+
33+
return compIssues;
34+
}
35+
36+
function fixUnsupported3DRendererIssue(compId: string): void {
37+
const comp = app.project.itemByID(parseInt(compId, 10));
38+
if (!(comp && comp instanceof CompItem)) {
39+
return;
40+
}
41+
42+
comp.renderer = RendererType.CLASSIC_3D;
43+
}
44+
45+
function fixUnsupported3DRendererIssues(compIds: string[]) {
46+
app.beginUndoGroup('fix unsupported 3d renderer');
47+
48+
for (const compId of compIds) {
49+
const comp = app.project.itemByID(parseInt(compId, 10));
50+
if (comp && comp instanceof CompItem) {
51+
comp.renderer = RendererType.CLASSIC_3D;
52+
}
53+
}
54+
55+
app.endUndoGroup();
56+
}
57+
58+
export {
59+
validateComps,
60+
fixUnsupported3DRendererIssue,
61+
fixUnsupported3DRendererIssues,
62+
};
Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,23 @@
1-
import { checkTextLayers, fixAllCapsIssue } from './textValidators';
2-
import { type AnyProjectIssue, ProjectIssueType } from './types';
1+
import type { AnyProjectIssue } from 'plainly-types';
2+
import { getAllComps } from '../utils';
3+
import { fixUnsupported3DRendererIssue, validateComps } from './compValidators';
4+
import {
5+
fixAllCapsIssue,
6+
fixAllCapsIssues,
7+
validateTextLayers,
8+
} from './textValidators';
9+
10+
enum ProjectIssueType {
11+
AllCaps = 'AllCaps',
12+
Unsupported3DRenderer = 'Unsupported3DRenderer',
13+
}
314

415
function validateProject(): string {
5-
const textIssues = checkTextLayers();
6-
let issues: AnyProjectIssue[] = [];
16+
const comps = getAllComps(app.project);
717

8-
if (textIssues.length > 0) {
9-
issues = issues.concat(textIssues);
10-
}
18+
const textIssues = validateTextLayers(comps);
19+
const compIssues = validateComps(comps);
20+
const issues: AnyProjectIssue[] = [...textIssues, ...compIssues];
1121

1222
if (issues.length > 0) {
1323
return JSON.stringify(issues);
@@ -17,14 +27,19 @@ function validateProject(): string {
1727
}
1828

1929
function fixAllIssues(issues: AnyProjectIssue[]) {
30+
app.beginUndoGroup('fix all');
31+
2032
for (let i = 0; i < issues.length; i++) {
2133
const issue = issues[i];
2234
if (issue.type === ProjectIssueType.AllCaps) {
2335
fixAllCapsIssue(issue.layerId);
2436
}
37+
if (issue.type === ProjectIssueType.Unsupported3DRenderer) {
38+
fixUnsupported3DRendererIssue(issue.compId);
39+
}
2540
}
2641

27-
validateProject();
42+
app.endUndoGroup();
2843
}
2944

30-
export { validateProject, fixAllIssues };
45+
export { validateProject, fixAllIssues, fixAllCapsIssues, ProjectIssueType };

plainly-aescripts/src/validation/textValidators.ts

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { getAllComps, getTextLayersByComp } from '../utils';
2-
import { ProjectIssueType, type TextLayerIssues } from './types';
1+
import type { TextLayerIssues } from 'plainly-types';
2+
import { getTextLayersByComp } from '../utils';
3+
import { ProjectIssueType } from '.';
34

4-
function checkTextLayers(): TextLayerIssues[] {
5-
const comps = getAllComps(app.project);
6-
const textLayers: TextLayerIssues[] = [];
5+
function validateTextLayers(comps: CompItem[]): TextLayerIssues[] {
6+
const textLayersIssues: TextLayerIssues[] = [];
77

88
for (let i = 0; i < comps.length; i++) {
99
const comp = comps[i];
@@ -24,11 +24,10 @@ function checkTextLayers(): TextLayerIssues[] {
2424
// this checks only the first character of the text layer
2525
// IMPORTANT: with this method, on older versions (< 24.3), we can't fix, but we can at least report it
2626
if (textDocument.allCaps) {
27-
textLayers.push({
27+
textLayersIssues.push({
2828
type: ProjectIssueType.AllCaps,
2929
layerId: layer.id.toString(),
3030
layerName: layer.name,
31-
text: true,
3231
});
3332
continue;
3433
}
@@ -52,11 +51,10 @@ function checkTextLayers(): TextLayerIssues[] {
5251
cRange.fontCapsOption === FontCapsOption.FONT_ALL_CAPS
5352
) {
5453
hasCharacterAllCaps = true;
55-
textLayers.push({
54+
textLayersIssues.push({
5655
type: ProjectIssueType.AllCaps,
5756
layerId: layer.id.toString(),
5857
layerName: layer.name,
59-
text: true,
6058
});
6159
break;
6260
}
@@ -73,17 +71,16 @@ function checkTextLayers(): TextLayerIssues[] {
7371
range.isRangeValid &&
7472
range.fontCapsOption === FontCapsOption.FONT_ALL_CAPS
7573
) {
76-
textLayers.push({
74+
textLayersIssues.push({
7775
type: ProjectIssueType.AllCaps,
7876
layerId: layer.id.toString(),
7977
layerName: layer.name,
80-
text: true,
8178
});
8279
}
8380
}
8481
}
8582

86-
return textLayers;
83+
return textLayersIssues;
8784
}
8885

8986
/**
@@ -138,10 +135,28 @@ function fixAllCapsIssue(layerId: string) {
138135
updateLayerTextDocument(layer, newValue);
139136
}
140137

138+
/**
139+
* Fixes all caps issues for multiple text layers in a single undo group.
140+
*
141+
* **NOTE**: Works only on `After Effects 24.3` and later due to the use of `characterRange` method and `fontCapsOption`.
142+
*
143+
* @param layerIds Array of layer IDs to fix
144+
* @returns The name of the undo group created, or undefined if no fixes were applied
145+
*/
146+
function fixAllCapsIssues(layerIds: string[]) {
147+
app.beginUndoGroup('fix all caps');
148+
149+
for (const layerId of layerIds) {
150+
fixAllCapsIssue(layerId);
151+
}
152+
153+
app.endUndoGroup();
154+
}
155+
141156
function updateLayerTextDocument(layer: TextLayer, newValue: TextDocument) {
142157
const originalLayerName = layer.name;
143158
layer.sourceText.setValue(newValue);
144159
layer.name = originalLayerName; // Preserve original layer name
145160
}
146161

147-
export { checkTextLayers, fixAllCapsIssue };
162+
export { validateTextLayers, fixAllCapsIssue, fixAllCapsIssues };

plainly-aescripts/src/validation/types.ts

Lines changed: 0 additions & 32 deletions
This file was deleted.

plainly-plugin/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
"lucide-react": "^0.544.0",
2626
"react": "^18.3.1",
2727
"react-dom": "^18.3.1",
28-
"react-hooks-global-state": "^2.1.0"
28+
"react-hooks-global-state": "^2.1.0",
29+
"semver": "^7.7.4"
2930
},
3031
"devDependencies": {
3132
"@babel/core": "^7.28.6",
@@ -42,6 +43,7 @@
4243
"@types/node": "^20.17.6",
4344
"@types/react": "^18.3.12",
4445
"@types/react-dom": "^18.3.1",
46+
"@types/semver": "^7.7.1",
4547
"autoprefixer": "^10.4.20",
4648
"babel-jest": "^30.2.0",
4749
"babel-loader": "^9.2.1",

0 commit comments

Comments
 (0)