Here you go, a rough demo of what you want.
It bypasses user input and gets an entry
for the opened file’s parent folder using getEntryWithUrl()
and then generates a session token for each jpeg as it saves them.
const { app, core } = require("photoshop");
const batchPlay = require("photoshop").action.batchPlay;
const lfs = require("uxp").storage.localFileSystem;
try {
// Helper functions
const getParentFolderEntry = async (filepath) => {
const folderPath = path.dirname(filepath);
return await lfs.getEntryWithUrl(folderPath);
};
const getAnimationFrameCount = async () => {
return await batchPlay(
[
{
_obj: "get",
_target: [
{
_property: "frameCount",
},
{
_ref: "animationClass",
_enum: "ordinal",
_value: "targetEnum",
},
],
_options: {
dialogOptions: "dontDisplay",
},
},
],
{}
);
};
const selectAnimationFrame = async (index) => {
return await batchPlay(
[
{
_obj: "select",
_target: [
{
_ref: "animationFrameClass",
_index: index,
},
],
_options: {
dialogOptions: "dontDisplay",
},
},
],
{}
);
};
const makeSnapshot = async (name) => {
return batchPlay(
[
{
_obj: "make",
_target: [
{
_ref: "snapshotClass",
},
],
from: {
_ref: "historyState",
_property: "currentHistoryState",
},
name: name,
using: {
_enum: "historyState",
_value: "fullDocument",
},
_isCommand: true,
_options: {},
},
],
{}
);
};
const revertToSnapshot = async (name) => {
return batchPlay(
[
{
_obj: "select",
_target: [
{
_ref: "snapshotClass",
_name: name,
},
],
_isCommand: true,
_options: {
dialogOptions: "dontDisplay",
},
},
],
{}
);
};
const deleteSnapshot = async (name) => {
return batchPlay(
[
{
_obj: "delete",
_target: [
{
_ref: "historyState",
_name: name,
},
],
_isCommand: true,
_options: {
dialogOptions: "dontDisplay",
},
},
],
{}
);
};
const saveJPEG = async (saveFolder, newFilename, quality) => {
try {
const doc = app.activeDocument;
const theNewFile = await saveFolder.createFile(newFilename, {
overwrite: true,
});
const saveFile = await lfs.createSessionToken(theNewFile);
return batchPlay(
[
{
_obj: "save",
as: {
_obj: "JPEG",
extendedQuality: quality,
matteColor: {
_enum: "matteColor",
_value: "none",
},
},
in: {
_path: saveFile,
_kind: "local",
},
documentID: doc.id,
lowerCase: true,
saveStage: {
_enum: "saveStageType",
_value: "saveBegin",
},
_isCommand: true,
_options: {
dialogOptions: "dontDisplay",
},
},
],
{}
);
} catch (error) {
console.log(error);
}
};
// Main process
await core.executeAsModal(async () => {
const file = await lfs.getFileForOpening();
const saveFolder = await getParentFolderEntry(file.nativePath);
await app.open(file);
const doc = app.activeDocument;
const baseFilename = doc.name.slice(0, doc.name.lastIndexOf("."));
const countFrames = await getAnimationFrameCount();
const frameCount = countFrames[0].frameCount;
for (let i = 0; i < frameCount; i++) {
const newFilename = `${baseFilename}_${i + 1}`;
await selectAnimationFrame(i + 1);
await makeSnapshot(newFilename);
doc.flatten();
await saveJPEG(saveFolder, newFilename, 12);
await revertToSnapshot(newFilename);
await deleteSnapshot(newFilename);
}
});
} catch (error) {
console.error(error);
console.trace(error);
}
You’ll also need to have your manifest
conform to v5 like so:
{
"host": [
{
"app": "PS",
"minVersion": "24.6.2",
"data": {
"apiVersion": 2
}
}
],
"manifestVersion": 5,
"requiredPermissions": {
"localFileSystem": "fullAccess"
}
// ...all other manifest properties
}