-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathAeScriptsApi.ts
More file actions
301 lines (268 loc) · 8.65 KB
/
AeScriptsApi.ts
File metadata and controls
301 lines (268 loc) · 8.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
import { isEmpty } from '@src/ui/utils';
import type {
AnyProjectIssue,
InstalledFontData,
ProjectData,
ProjectInfo,
ProjectIssueType,
RelinkData,
} from 'plainly-types';
import { csInterface } from '../constants';
async function evalScriptAsync(func: string): Promise<string | undefined> {
return new Promise((resolve, reject) => {
try {
const finalFunc = `$['com.plainlyvideos.after-effects-plugin.Panel'].${func};`;
csInterface.evalScript(finalFunc, (result: string) => {
if (result.includes('Error: ')) {
reject(new Error(result));
}
resolve(result === 'undefined' ? undefined : result);
});
} catch (error) {
reject(error);
}
});
}
/**
* Typed API bridge for communicating with After Effects ExtendScript.
* Wraps the PlainlyAE namespace exposed by the aescripts bundle.
*/
class AeScriptsApiClass {
/**
* Opens a folder selection dialog in After Effects.
* @returns The selected folder path, or undefined if cancelled
*/
async selectFolder(): Promise<string | undefined> {
return await evalScriptAsync('selectFolder()');
}
/**
* Collects InstalledFontData by PostScript name from After Effects.
* @param postScriptName The PostScript name of the font to look up
* @returns An array of InstalledFontData, or undefined if not found
*/
async getInstalledFontsByPostScriptName(
postScriptName: string,
): Promise<InstalledFontData[] | undefined> {
const result = await evalScriptAsync(
`getInstalledFontsByPostScriptName(${JSON.stringify(postScriptName)})`,
);
if (!result) return undefined;
try {
return JSON.parse(result);
} catch {
throw new Error('Failed to parse fonts data.');
}
}
/**
* Collects InstalledFontData by Family name and Style name from After Effects.
* @param familyName The family name of the font to look up
* @param styleName The style name of the font to look up
* @returns An array of InstalledFontData, or undefined if not found
*/
async getInstalledFontsByFamilyNameAndStyleName(
familyName: string,
styleName: string,
): Promise<InstalledFontData[] | undefined> {
const result = await evalScriptAsync(
`getInstalledFontsByFamilyNameAndStyleName(${JSON.stringify(familyName)}, ${JSON.stringify(styleName)})`,
);
if (!result) return undefined;
try {
return JSON.parse(result);
} catch {
throw new Error('Failed to parse fonts data.');
}
}
/**
* Checks if a font is installed in After Effects by PostScript name, family name, and style name.
* @param postScriptName The PostScript name of the font to check
* @param familyName The family name of the font to check
* @param styleName The style name of the font to check
* @returns True if the font is installed, false otherwise
*/
async isFontInstalled(
postScriptName: string,
familyName: string,
styleName: string,
): Promise<boolean> {
let fonts = await this.getInstalledFontsByPostScriptName(postScriptName);
if (isEmpty(fonts)) {
fonts = await this.getInstalledFontsByFamilyNameAndStyleName(
familyName,
styleName,
);
if (fonts === undefined) {
// Could not get fonts by either methods, continue
return true;
}
if (isEmpty(fonts)) {
return false;
}
}
if (fonts[0]?.isSubstitute) {
return false;
}
return true;
}
/**
* Collects all fonts and footage from the current After Effects project.
* @returns Project information including fonts and footage arrays
*/
async collectFiles(): Promise<ProjectInfo> {
const result = await evalScriptAsync('collectFiles()');
if (!result) throw new Error('Failed to collect files');
try {
return JSON.parse(result);
} catch {
throw new Error('Failed to parse collected files data.');
}
}
/**
* Sets Plainly project metadata in the After Effects project XMP.
* @param id - The Plainly project ID
* @param revisionCount - The project revision count
*/
async setProjectData(id: string, revisionCount: number): Promise<void> {
await evalScriptAsync(
`setProjectData(${JSON.stringify(id)}, ${JSON.stringify(revisionCount)})`,
);
}
/**
* Retrieves Plainly project metadata from the After Effects project XMP.
* @returns Project data including documentId, id, and revisionCount
*/
async getProjectData(): Promise<ProjectData | undefined> {
const result = await evalScriptAsync('getProjectData()');
if (!result) return undefined;
try {
return JSON.parse(result);
} catch {
throw new Error('Failed to parse project data.');
}
}
/**
* Removes Plainly project metadata from the After Effects project XMP.
*/
async removeProjectData(): Promise<void> {
await evalScriptAsync('removeProjectData()');
}
/**
* Gets the file system path of the current After Effects project.
* @returns The project file path, or undefined if project is not saved
*/
async getProjectPath(): Promise<string | undefined> {
return await evalScriptAsync('getProjectPath()');
}
/**
* Saves the current After Effects project.
*/
async saveProject(): Promise<void> {
await evalScriptAsync('saveProject()');
}
/**
* Gets the version of After Effects.
* @returns The version of After Effects as a string
*/
async getAfterEffectsVersion(): Promise<string> {
const result = await evalScriptAsync('getAfterEffectsVersion()');
if (!result) throw new Error('Failed to get After Effects version');
return result;
}
/**
* Returns all layer names in a given After Effects composition.
* @param compId - The ID of the composition
* @returns An array of layer names
*/
async getCompLayerNames(compId: number): Promise<string[]> {
const result = await evalScriptAsync(
`getCompLayerNames(${JSON.stringify(compId)})`,
);
if (!result) return [];
try {
return JSON.parse(result);
} catch {
throw new Error('Failed to parse layer names data.');
}
}
/**
* Relinks footage items in the After Effects project to new file paths.
* @param relinkData - Object mapping item IDs to new file paths
*/
async relinkFootage(relinkData: RelinkData): Promise<void> {
await evalScriptAsync(`relinkFootage(${JSON.stringify(relinkData)})`);
}
/**
* Selects a layer in the current After Effects composition by its ID.
* @param layerId - The ID of the layer to select
*/
async selectLayer(layerId: string): Promise<void> {
await evalScriptAsync(`selectLayer(${JSON.stringify(layerId)})`);
}
/**
* Selects a composition in After Effects by its ID.
* @param compId - The ID of the composition to select
*/
async selectComp(compId: string): Promise<void> {
await evalScriptAsync(`selectComp(${JSON.stringify(compId)})`);
}
/**
* Selects a file item in the After Effects project by its ID.
* @param fileId - The ID of the file item to select
*/
async selectFile(fileId: string): Promise<void> {
await evalScriptAsync(`selectFile(${JSON.stringify(fileId)})`);
}
/**
* Validates the current After Effects project for Plainly issues.
* @returns An array of validation issues, or an empty array if no issues found
*/
async validateProject(): Promise<AnyProjectIssue[]> {
const result = await evalScriptAsync('validateProject()');
if (!result) return [];
try {
return JSON.parse(result);
} catch {
throw new Error('Failed to parse validation results.');
}
}
/**
* Fixes all provided Plainly issues in the After Effects project.
* @param issues - Array of project issues to fix
*/
async fixAllIssues(
issues: AnyProjectIssue[],
ignoreFixing: {
[key in ProjectIssueType]?: boolean;
},
): Promise<void> {
await evalScriptAsync(
`fixAllIssues(${JSON.stringify(issues)}, ${JSON.stringify(ignoreFixing)})`,
);
}
/**
* Fixes unsupported 3D renderer issues for multiple compositions
* @param compIds - Array of composition IDs to fix
*/
async fixUnsupported3DRendererIssues(compIds: string[]): Promise<void> {
await evalScriptAsync(
`fixUnsupported3DRendererIssues(${JSON.stringify(compIds)})`,
);
}
}
/**
* Singleton instance of the AeScriptsApi.
* Use this to make typed calls to After Effects ExtendScript functions.
*
* @example
* ```typescript
* import { AeScriptsApi } from './bridge/AeScriptsApi';
*
* // Save project
* await AeScriptsApi.saveProject();
*
* // Get project data
* const projectData = await AeScriptsApi.getProjectData();
* console.log(projectData.id);
* ```
*/
export const AeScriptsApi = new AeScriptsApiClass();