Run extendscript from uxp

Hi,

Is there a way to run extendscript (jsx) code from a uxp (js) file?
I’ve been running into a LOT of holes and major flaws/issues with uxp that I KNOW for a fact I could do in extendscript which makes uxp almost unusable…

1 Like

If there is a way to do that then it will be most likely removed. So advice would be don’t mix it together.

Could you please name some of those flaws so it could be fixed? :slight_smile:

Heyo, I’m aware that the plan is to remove CEP but since I’m in a studio setting we can lock version if we actually need to make that not happen.

Docs feel like they are lacking for starters.

The manifest 5 doc saying

  • fullAccess: Allows the plugin to inspect, modify, and delete files to which you have access on all volumes attached to this device. The user will be required to consent before installation or update.

but then giving no actual indication of how to perform the usage as the docs for filesystem don’t explain it outside of the ones that were already there which don’t allow you to access the full file system (along side another topic from a month ago asking how to do it Access arbitrary file )

One of my current frustrations is that I need to automate saving a psd + clut ideally without user intervention as I know where it needs to live but that doesn’t seem possible via uxp but I know for a fact it’s possible in extendscript as I wrote CEP extensions specifically to handle auto saving various image types out.

Even with batchplay “seemingly” being there to bridge the gap in some ways, I would have expected that getting an actionJSON for saving a psd and being able to plug that into batchplay should work but it doesn’t and throws other totally undocumented errors… and I expected that one of the issues was just having to fill in the activeDoc.id as the id but that didn’t work either
const saveresult = await executeAsModal(async () => { // In the modal state now const result = await app.batchPlay( [ {"_obj":"save","as":{"_obj":"photoshop35Format","maximizeCompatibility":true}, "documentID":app.activeDocument.id, "in":{"_kind":"local","_path":"D:/MyFolder/My.psd"}, "lowerCase":true } ]); });
From what I’ve seen around the forums/docs there obviously should be a “token” in the _path but making that doesn’t seem to work from the forum post from Adobe itself either…

The docs website search function seems to be completely broken
Proof : even typing in “storage” returns no results which is obviously wrong (since before I searched this I was ON the storage page…)

I had an example before this which was even worse, as it said it had results but clicking on the left bar to show them still said “no results” which makes absolutely 0 sense…

Not trying to be in a ranty mood, but I spent years trying to sort out extendscript’s api and when the thing replacing it feels like it’s in a worse state after a while of being out it just feels like we should have some more movement on it to at least bring it to parity.

Maybe some more work on making it not just “push things through batchPlay cause people were fine having to hate life and use actionDescriptors/the listener plugin to code” and actually having functions to call directly on doc/layer etc. as we did before (and in most api’s)…Even if that at a low level is just wrapping a batchPlay call if it makes working in PS plugin land better it feels like a worthwhile investment…

2 Likes

To handle automated saving for plugins my approach is to build a settings page (I usually render it in a dialog triggered from the flyout menu), which has a file picker to define a root working folder. I then create a persistent token with the result and write it to a .json file in the plugin storage folder. That only needs to be done once on plugin install and then via that token I have full access permissions for that folder and all its descendants.
That settings.json file also stores any other persistent settings for the plugin and is loaded on plugin initialisation.

You have to programmatically generate new tokens for specific save functions etc, but with that root level persistent token it can all be done silently and without user input.
This thread is how I figured it out:

In my personal use case I have the folder set as the root of an internal HDD that I only use for file storage. For an enterprise client I’ve used the same approach to allow them to define multiple network drives simultaneously (e.g. web-ready, design assets, archive, etc).

2 Likes

So that would work but I do really need to be able to save without intervention from the user as we need to make sure naming conventions are respected and that the psd/output files are named explicitly. If I make it so a user can choose a folder or even a file name it can break a lot of pipeline that will make a massive headache for not only me, but a very large amount of other people.
We have a bunch of env variables and such that we can set/read to know where the folder we want to output is it’s just a matter of PS making it so I can actually access it via UXP (again, this is trivial in extendscript…)
I also tried that code blob last week and it didn’t work for me? getItem isn’t a thing from the

const thePersistentFolderToken = localStorage.getItem("persistentFolder"); const thePersistentFolder = await fs.getEntryForPersistentToken(thePersistentFolderToken); const theNewFile = await thePersistentFolder.createFile("export.psd", {overwrite: true}); const saveFile = await fs.createSessionToken(theNewFile);
and I tried fixing it up so it was fs.getItem (as I have
const fs = require("uxp").storage.localFileSystem;
already…

1 Like

Thinking out loud here, but as you know the output path in advance you could compare the result of the file picker against it and throw an error if they don’t match and disable any damaging functionality.

I guess that could work but again, it’s very not ideal as it hinders speed, makes it so users have to click/hit enter etc. for no reason and in our case will have to manually go find the folder we want to save to rather than it just happening…it’s TERRIBLE ux something as used as PS…

More examples of terrible docs
https://developer.adobe.com/photoshop/uxp/uxp/reference-js/Modules/uxp/Persistent%20File%20Storage/FileSystemProvider/#getfileforopeningoptions

const files = await fs.getFileForOpening({allowMultiple: true, types: fileTypes.images});
if (files.length === 0) {
    // no files selected
}

fileTypes like that isn’t a thing and sure, much farther down the page it is mentioned by this is terrible docs since it expects that the reader already knows it’s a thing rather than actually providing a useful/correct code snippet…
And again, I get that as a living product docs are hard to keep up to date but this is silly that it seems all the doc snippets are rife with bad/non useable example code…

At least in the pdf’s for extendscript the only thing that usually was missing was what some functions actually wanted passed in, but at least then you could use the ESTK to debug that…

I agree that it’s far from ideal for enterprise development, but it can produce the result you want. There was talk at a previous Adobe Developer Day (and I think here on the forum too) about less restrictive permissions for enterprise but nothing concrete.

I’m not sure you fully understand my process though - that folder picker is only used once ever (provided the path doesn’t change).
In normal day-to-day operation the plugin loads with Photoshop, grabs the settings file and root level persistent token and it’s good to go, no user input needed.

I can pull some code for you later this evening - I’d need to edit it down for clarity.

The path “can” change depending on what we’re exporting.
I’m trying to get this sorted for multiple tools that are either being built or will be need to be built. In some cases the user will need to just save the output beside the psd, some will need to save it a folder up and into a sibling folder.
I figured the less restrictive would be the fullAccess thing I listed in my first reply about the manifest update? It does say that obviously the user needs to ok, which ours will as they’re internal and we’re not distributing to many people outside of outsourcers etc.

I’d love to have some example code that works for sure and I’d be very appreciative if you did that :slight_smile:

If you have a persistent parent folder token then you don’t need new folder picker dialogs for any nested subfolder. So if you would do it for e.g. C: drive root folder then anything inside would be free of user intervention.
This is due to security since any JSX file could in theory read anything inside your computer and search e.g. for file like passwords.txt anywhere in PC :smiley: :-/ So there is user consent to limit this range.

I guess that would be a work around…I get the security risks for it but that’s extremely annoying for so many practicality reasons. It would almost certainly render my Lazy Save plugin completely dead if I tried to re do it in UXP (which I did consider)

So what you’re saying is that on first use of a plugin I need to get the user to select a folder that I can then have access to for saving/trawling where I want to save things in the future and I can save the token for future use, so they don’t have to do it each time?

(also just realized that you made Alchemist which has already helped quite a bit. Cheers for that)

Oh yeah one other question which I assume due to the “secure” nature of uxp is going to not work…is there a way to actually call an exe from a JS file? We have some build tools that we need to run to actually build our files for our specific uses and in every other DCC that we use (maya/painter etc) it’s easy to do…it’s looking like PS is going to be the outlier…

Edit: I feel like maybe I misread or had a stroke as I’ve just looked at the uxp docs again and where I thought it said there was plans to remove CEP/Extenscript it says there isn’t. I’m going to just go down that route as I have loads of exp with that and loads of boilerplate to dev it and doing UXP would be nice but is far too much of a headache atm.

Here you go, it’s a little rough around the edges (no error checking etc), but here’s a basic demo of my approach.

2 Likes