In this episode I approach BatchPlay, the super-powerful Photoshop-only scripting technique that can overcome DOM’s limits. This is part 1, at least one more video will follow.
If you find this content useful, please consider supporting me – 2020 is a hell of a year. I don’t have a Patreon page, but I’ve got two fairly cheap plugins on the Photoshop Marketplace, ALCE (Advanced Local Contrast Enhancer) and Double USM (on sharpening). If you happen to buy them, please leave a positive rating/review, it would greatly help. Or, you can
If you cannot, or don’t want to, that’s OK anyway.
Stay safe and thanks! 🙏🏻
The whole series so far
- #01 – Rundown on the UXP announcement @ the Adobe MAX 2020
- #02 - Documentation
- #03 - UXP Developer Tool
- #04 - Commands vs. Panels and the manifest.json
- #05 - Sync vs. Async code in Photoshop DOM Scripting
- #06 - BatchPlay (part 1): the ActionManager roots
- #07 - BatchPlay (part 2): Alchemist as a UXP Script Listener
- #09 - BatchPlay (part 3): Alchemist as a UXP Inspector
- #09 - Adobe Spectrum UXP
The transcripted video looks weird, but I’m told it helps to have it anyway because it’s easier to translate to other languages. Apologies for the lack of punctuation and the sloppy syntax.
Hey I’m Davide and this is Adobe UXP things you need to know! Today I’m finally talking about BatchPlay. So BatchPlay is one of the most important subjects of the whole series especially given the state of UXP and the new scripting engine in Photoshop. In this first part, because I think there’s going to be at least two, I’m going to cover BatchPlay roots i.e. ActionManager code because they’re strictly related. In my experience at least things are much easier to deal with and way less scary than at first sight if you understand their inner mechanisms and this is particularly true with ActionManager code. I’m going to give you a very condensed version of it, there’s much more to be said and I will point you to a more comprehensive resource later on. As far as all the Adobe apps are concerned scripting is whatever it takes to get and set the app object’s various properties and nested properties and calling their methods. What is generally dubbed DOM scripting where DOM stands for the document object model i.e the way the props and methods are modelled in the sort of Russian doll fashion, well that DOM scripting is scripting for all the Adobe apps. If you need a prop that is not available via the DOM then it’s outside the domain of scripting, period. Forget about it. So this is true for every Creative Cloud app that supports scripting except Photoshop, which is special and it has always been special at least since version what was it four or five and by the way not CS4 like just four, so way way back. Photoshop has something called ActionManager that is one of the ugliest unfamiliar ExtendScript code ever but that lets you do things that are outside of the DOM scope. So when we talk about the DOM coverage we actually mean the amount of scope that the Document Object Model encompasses, okay? The things you can do versus the ActionManager superset or the things you can do with scripting. So think about it this way: you have Photoshop, the entire applications and its features, and Photoshop is going to be the super set of things that a user can do, and inside it there is a subset that I’m going to draw in a different color that is the amount of things that a user can do with scripting. So this is the scripting subset and with other Adobe applications such as InDesign, Illustrator this scripting corresponds to the DOM scripting but in Photoshop this superset corresponds to the ActionManager scripting because this is always going to be a super set of the DOM scripting, which in turn is a smaller subset of that so this is the DOM. So when we talk about DOM coverage in Photoshop we can mean at least two different things: so the difference that exists between the DOM and the outer ActionManager layers so the amount of things that ActionManager can do and the DOM can’t but also the amount of things that we would like to be able to reach with the DOM in the app itself so this area here these are the props and methods that scripting cannot reach for instance, let me think about it, the values that are in the Info Palette the live reading these are not reachable by scripting, not ActionManager nor the DOM. But usually we compare the DOM against the ActionManager because we tend to think that ActionManager is kind of the ceiling. So how do you get ActionManager code? So there is this thing called ScriptListener plugin that you can find at this page and I think the download is right here you install this by moving the folder into the Photoshop / plugins folder and then upon restarting everything that you do or everything that is recordable by the ScriptListener plugin is logged on the desktop in a file so I happen to have mine with a shortcut here and this is the super ugly code that you can get now. The key point here is that this code strange as it might seem is playable so you can get this and run it in your Photoshop and that’s going to perform the same things that I did when I recorded that and there is a parallel between recording ActionManager code and recording actions – the kind of actions that are in the Action Palette like these ones, and ActionManager has action in its name for a reason so in Photoshop 4 in order to support this new feature so the possibility to record and playback actions the engineering team has built an event system that is triggered every time the user interacts with the Photoshop UI, that is to say every time you select a tool or interact with the layers or whatnot. And what happens is that those interactions will trigger an event, a very specific event; there are not many of them but each one of those events carry a payload in the form of a strange weird complex object called the Descriptor, that takes into account all the information that describes and give meaning to that single event so I happen to have this very code so the output of the ScriptListener plugin in Atom and the reason is that is syntax highlighted and it’s easier to inspect so let’s try to have an intuition about this code trying to make sense of it this is the raw output and as you see it’s kind of scary the very first step towards a proper understanding of it is to install a tool that is able to beautify the kind of code and you can find it it’s made by this guy Thomas Sinkunas I’m not sure I can pronounce his name he is from Lithuania and the repository should be at the end of the list and it’s called Clean SL you download it and you install it by moving the files into the Photoshop / presets / scripts folder and then you can find it in Photoshop in the file scripts menu hits here Clean SL and you have the original output of the ScriptListener on the left and based on the parameters you can set here you can clean the code and end up with something that looks much better. The version that I have here has been processed a little bit more basically name substitution and this is it so let’s try to make sense of it in a very intuitive way what you have to know that the event that I was talking about earlier is in this executeAction here and each one of these are steps so this is one step this is another step this is a very long third step and these are things that I’ve done in Photoshop before and let’s try to see if we can make sense of them so as I said this ExecuteAction is what triggers the event so this event was an open event then you have a copy to layer event and if you scroll down this is a curves event and then you have a select and another select and so on and so forth so try not to focus too much on those s2t functions here because I’m not going to cover here what StringIDs are or TypeIDs let’s just think in a very intuitive way that we are opening something that has a path because we’re putting a path somewhere and the path is this one and then we have other stuff that is hard to make sense of don’t record and force notify what are those? have no idea. And everything is stored inside of this ActionDescriptor object so again you can think about ActionManager as an event which is this open here that is followed by a Descriptor and the Descriptor is a complex object that takes into account all the parameters that the event needs in order to be played back so even if you don’t understand probably everything here you can assume that I was opening a document called Anita.jpg right and in the next action or the next step there is just one event that is copy to layer which is one of the many ways that ActionManager has to duplicate a layer you have at least two or three at the very least and copy to layer is one so you’re just duplicating a layer and this corresponds not to a duplicate event as you might assume but a copyToLayer one and it has no Descriptor so you see that the place of Descriptor is undefined you don’t need any additional information in order to duplicate a layer and the third action is a very very complex one you have one two three four five six ActionDescriptors and in every ActionManager code you have the event as I said that is the one that is passed as the first argument to the ExecuteAction function so the event is “curves” let’s put it here event curves and then just one ActionDescriptor so you have one ad but this one ActionDescriptor can contain as many things as Adobe wants or need in this case you have five inner ActionDescriptors so you have something like five ADs here and each one of these ADs contains a variety of other things one ActionDescriptor can contain another ActionDescriptor so you have a kind of Russian doll structure and you don’t really have to understand why those ActionDescriptors are nested that way because this is outside our reach so we can just observe the way that the ScriptListener code is presented to us i.e we can just note that in order to draw a curve so to create an adjustment of type curve you have to structure the ActionDescriptor in this very way so again without trying to inspect it properly you can do it it’s not very difficult after all but you see that we have channels and we are probably acting on the composite and we have this “paint” which is quite strange looks like a typo for a “point” and you might not be too far from the truth in this case with horizontal and vertical points so this curve probably has a one two three and four points in the array of points in fact it was a curve like that so an S shaped curve and you have one point here one point there and a couple of points in the middle so zero, zero, two-five-five, two-five-five the end points and the two points in the middle here and then what um a series of different objects that are ActionLists and ActionLists you can think about them as arrays of ActionDescriptor so whatever it is that it’s needed for this ActionDescriptor in order to create and replay a curves adjustment it’s a very very complicated one so let’s scroll down and you see here a select event again in the ExecuteAction which is matched here as well and in this case we are selecting I was selecting a tool um the move tool here and the type creation tool so I was trying to create a text layer and then again a move tool in this case and then the following step is an awfully long one I think it’s like I don’t know what 200 lines of code or something you see that that’s kind of crazy isn’t it, the beauty of ActionManager code. In this case you have 18 ActionDescriptors so one main ActionDescriptor that contains at least 17 of them nested inside it plus a four ActionList in the ActionReference takes into account what is the target of the entire step this again is awfully long and awfully complicated and there’s no way that we can understand it uh just you know note that it’s this way and probably this is setting a lot of options that are, well, optional as the, I don’t know, the base line shift or the horizontal scale or the fact that the text has or not a strikethrough and so on and so forth but in the end the event is a “make” event and accepts just one ActionDescriptor and then there’s a “move” event here so I’ve offset the layer these values on the horizontal and vertical axis and you see that I’m somehow acting on a layer because you read layer here again we’re working with intuition right now so we’re trying to look at things and figure out what might have happened and we have put that object the one that takes into account these values into an offset and then executing the event according to that ActionDescriptor that contains all the information and finally I’ve closed the file and this is kind of funny so the saving property is an enumeration of class “yesNo” and I’ve chosen no so I’ve closed without saving so whatever this is it’s the way that Photoshop internally deals with those actions that I was, as a user, performing and this is pretty valid code so I can run this code and perform the same step so I can open the file and then duplicate the layer and so on and so forth so this is the whole point of having the ScriptListener code and some of those actions are not available through the DOM I doubt that you can create a text layer via the DOM and I also doubt you can create a curve adjustment probably the curve has been introduced in the last years might be wrong though anyways this is the ExtendScript code ActionManager now enters BatchPlay this is the equivalent of the code that you’ve just seen so let me put that on the side so on the right you have the BatchPlay on the left you have the ExtendScript so first thing to notice is the syntax you are using the BatchPlay function of the Action property of the Photoshop object so you have to require Photoshop and then Action and then BatchPlay and you see the BatchPlay takes an Array as an argument so the Array is this guy here right so this part is the argument of the BatchPlay function and also an object so let’s have a look at the documentation page for the BatchPlay that you can find I guess here so in the doc mini-site in the Photoshop API you go to advanced and BatchPlay so you see that you require Photoshop and then through the Action you have access to the BatchPlay function that accepts a Descriptor as an array of Descriptors and options I’ll go into that in detail in the following videos but for the time being let’s just focus on the different syntax so you see that for instance in this case of the layer duplication I have just one event copyToLayer and this copied layer is matched in this object here we have the same copied layer and if you don’t take into account the options that is don’t display which is exactly this dialog modes no the equivalent of the dialogue mode no and this synchronous execution set to true in the options object you see that they are quite similar the syntax is different it’s JSON-like but the structure is the same again let’s try to compare the curves which was quite scary with all those ActionDescriptors now the ActionDescriptors are substituted by objects so you had for instance here an ActionDescriptor with the channel and now you have an object with the channel with a reference here an enumeration here so here you have them both and a value of composite so this is the composite right and the curves which is this guy here was another Descriptor that contains an ActionList so sort of an array of ActionDescriptors and it’s exactly what you have here right so an array of ActionDescriptors i.e. objects where you have this paint which I guess was points but probably the two StringIDs point to the same TypeID if you know what I mean it’s okay if you don’t don’t worry and the points right way more readable this way and again if you discard the dialog options and the options object it’s much more compact and readable not much luck with the type object I think because this one is quite long as well because there are a lot of properties to set but you get the gist of it. So the takeaway for this first part is that BatchPlay is a new syntax and very important couple of new features on top of the ActionManager structure so the more you know about ActionManager the more you already know about ActionManager the easier will be the transition to BatchPlay and if you don’t know that much about ActionManager I’m afraid there aren’t that many sources except for the Photoshop Scripting book this guy here where I have 70 pages on ActionManager alone and I doubt you can find that much content anywhere else so ActionManager is the way that Photoshop has to take into account the user interaction with Photoshop so that that interaction can be stored into proper actions or scripting code to be played back in a later time and what you have to do as a scripter is to take note of the way that Photoshop stores that data in order to be able to write your own both to set parameters and run actions i.e tasks and also being able to extract data from Photoshop because one thing that I have not mentioned is that ActionManager sits in Photoshop and is there for you to access it so you can get properties that otherwise are impossible to get via DOM so layer’s properties, app’s properties and so on and so forth. BatchPlay is ActionManager: it’s an evolution of the ActionManager that we had in ExtendScript with notable new features for instance the possibility to run multiple events and their Descriptors as an Array which is way faster and we will see that in the next video. It has a brand new syntax based on JSON and it’s way more friendly the very last thing that I want to leave you with is a plugin for Photoshop called Alchemist that you can find either on GitHub at this page it’s made by Jaroslav Bereza one of the most talented guys in the Photoshop scripting community and a guy with a very strong sense of humour by the way or also in the Photoshop marketplace so let me try to find it right here so go get it there are differences between the plugin that you can find in the marketplace and the ones you can find on GitHub for development purposes I would suggest you to get the GitHub one but get this one as well and give five stars and a positive review because he greatly deserves that. I’ll cover Alchemist in depth in the next video, play with it in the meantime. I think this is all for this video if you’ve learned something new please consider supporting me, there is a donation button either on my website or in the youtube video description; also I have products in the marketplace. Thank you for watching and see you in the next one. Bye!