Copy link to clipboard
Copied
I am looking for a way to place linked photos directly from Lightroom.
After selecting a layer, I want to run my script which will do the following:
How can I recursively search down to find the file?
How can I place it as linked?
Replace
descriptor.putObject( stringIDToTypeID( "openAs" ), charIDToTypeID( "null" ), descriptor2 );
with
descriptor.putObject(stringIDToTypeID("openAs"), stringIDToTypeID("Adobe Camera Raw"), descriptor2 );
Copy link to clipboard
Copied
karpiyon wrote
I am looking for a way to place linked photos directly from Lightroom.
After selecting a layer, I want to run my script which will do the following:
- Get the layer name (this will be the linked filename + suffix, at this point dgn)
- Open a “browse” dialog using Folder.selectDialog to get the root path the file can be found in.
- Recursively search down and if fileName.dgn is found place it as linked and delete the current layer.
Or, convert the smart object in the active layer to linkedHow can I recursively search down to find the file?
How can I place it as linked?
How would what you wrote be directly from Lightroom?
If you select a layer you are in Photoshop Lightroom does not support Layers.
If you target a layer in Photoshop then use place. Place will create a Smart Object Layer above the current targeted layer and this new smart object layer will be Photoshop new current target. If you want the Placed image file to be linked you would use Place linked not place embedded. So the current layer would be a smart object layer with a linked image file in the layers object. If you delete the current layer you delete the smart object layer you just placed in. Lightroom had no part in this process. You may have saved the file you place in using Lightroom in the past. That was not part of your script's process.
Your script Process would put the Photoshop User into a select image file dialog. (Not a folder select dialog) The user would navigate to the file to want placed in and select that file. The Script would then use place Linked to place in the smart object layer with the linked image file. The layer will not be an independent smart object layer it will be linked to you master image file.
Copy link to clipboard
Copied
Let me rephrase...
In LR I select the image ans open as smart object in PS
In PS I run my script on the smart object layer.
The reason i do this is because i do not want to navigate to the location of the image and search for it - it is time consuming task.
In my script I specify the root folder and it looks for a file whose name is equal to the PS smart layer name.
Currently i have most of the script including the recursive search for the file.
What need is a was to insert/place a file as linked from withing PS by scripting.
Dan
Copy link to clipboard
Copied
The above is not done directly from LR but will save a lot for time.
Copy link to clipboard
Copied
OK I see you open you lightroom image in Photoshop as a smart object layer instead of a background image. When your in Photoshop with this smart object bottom layer you want a script to find the file that was open as an embedded object. Then Place the image in as a linked object in a smart object layer then delete the original opened embedded object layer.
I would be concerned that recursive searching folders will take some time and may find a file with duplicate file name, fine the wrong file as a match. Do you plain of verifying you found the correct image file?
To do the File Place you will most likely need to use action manager code coded as a function where you pass the full path of the file to be placed in. You would use action manager code for menu File>Place Linked.
scriptlistener code for place linked
// =======================================================
var idPlc = charIDToTypeID( "Plc " );
var desc14 = new ActionDescriptor();
var idIdnt = charIDToTypeID( "Idnt" );
desc14.putInteger( idIdnt, 3 );
var idnull = charIDToTypeID( "null" );
desc14.putPath( idnull, new File( "C:\\LO2G5037.CR2" ) );
var idLnkd = charIDToTypeID( "Lnkd" );
desc14.putBoolean( idLnkd, true );
var idOpAs = charIDToTypeID( "OpAs" );
var desc15 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
desc14.putObject( idOpAs, idnull, desc15 );
var idFTcs = charIDToTypeID( "FTcs" );
var idQCSt = charIDToTypeID( "QCSt" );
var idQcsa = charIDToTypeID( "Qcsa" );
desc14.putEnumerated( idFTcs, idQCSt, idQcsa );
var idOfst = charIDToTypeID( "Ofst" );
var desc16 = new ActionDescriptor();
var idHrzn = charIDToTypeID( "Hrzn" );
var idPrc = charIDToTypeID( "#Prc" );
desc16.putUnitDouble( idHrzn, idPrc, 0.000000 );
var idVrtc = charIDToTypeID( "Vrtc" );
var idPrc = charIDToTypeID( "#Prc" );
desc16.putUnitDouble( idVrtc, idPrc, 0.000000 );
var idOfst = charIDToTypeID( "Ofst" );
desc14.putObject( idOfst, idOfst, desc16 );
executeAction( idPlc, desc14, DialogModes.NO );
Code cleaned by Clean SL script
// =======================================================
placeEvent(3, new File( "C:\\LO2G5037.CR2" ), true, 0, 0);
function placeEvent(ID, null2, linked, horizontal, vertical) {
var descriptor = new ActionDescriptor();
var descriptor2 = new ActionDescriptor();
var descriptor3 = new ActionDescriptor();
descriptor.putInteger( stringIDToTypeID( "ID" ), ID );
descriptor.putPath( charIDToTypeID( "null" ), null2 );
descriptor.putBoolean( stringIDToTypeID( "linked" ), linked );
descriptor.putObject( stringIDToTypeID( "openAs" ), charIDToTypeID( "null" ), descriptor2 );
descriptor.putEnumerated( stringIDToTypeID( "freeTransformCenterState" ), stringIDToTypeID( "quadCenterState" ), stringIDToTypeID( "QCSAverage" ));
descriptor3.putUnitDouble( stringIDToTypeID( "horizontal" ), stringIDToTypeID( "percentUnit" ), horizontal );
descriptor3.putUnitDouble( stringIDToTypeID( "vertical" ), stringIDToTypeID( "percentUnit" ), vertical );
descriptor.putObject( stringIDToTypeID( "offset" ), stringIDToTypeID( "offset" ), descriptor3 );
executeAction( stringIDToTypeID( "placeEvent" ), descriptor, DialogModes.NO );
}
Copy link to clipboard
Copied
Yes, i'm aware of the fact i may find more than one candidate.
As for the search time, i allow the user to specify the root folder for search to reduce search time.
I attached my script below, assuming i find only one file, I am unable to "return" it from my recursion.
In this case i always get it as undefined.
SO i have 2 questions:
1. What do i need to change in order to make sure i get required search result,in this case: "foundFile"
I tried modifying the end recursive return traverseFolder(files, layerN); but that ruined all the function.
The best would be to return a global variable but i failed to do this as well.
2. how do i use the action manager code for menu File>Place Linked in my script once i have the full path to the required file.
This is my script so far:
var doc = app.activeDocument;
var fName = doc.name.split(".");
var fName = fName[0];
var layerN = doc.activeLayer.name;
var linkFolder = Folder.selectDialog("Selection prompt");
if(linkFolder !== null){
var linkFolderPath = linkFolder.path;
var linkName = linkFolder.name;
var linkFullPath = linkFolderPath+"/"+linkName;
//alert("Looking for " + layerN + " in\n" + linkFullPath);
function traverseFolder(path, layerN) {
var folder = new Folder(path);
suffix = new Array();
suffix[0] = "tif";
suffix[1] = "dng";
suffix[2] = "psd";
var files = folder.getFiles();
for (var i = 0; i < files.length; i++)
{
if (files instanceof File)
{
var fullPathName = Folder.decode(files);
var fileName = files.name;
fileName = decodeURI(fileName);
for(var s = 0; s < suffix.length ; s++){
search_name = layerN + "." + suffix;
search_NAME = layerN + "." + suffix.toUpperCase();
if ((fileName == search_name) || (fileName == search_NAME))
{
foundFile = fullPathName + "/" + fileName;
alert("Found: " + foundFile);
return foundFile ;
}
}
}
else
{
traverseFolder(files, layerN);
// return traverseFolder(files, layerN);
}
}
}
var foundFile = traverseFolder(linkFullPath, layerN);
alert("Found: " + foundFile);
}
Copy link to clipboard
Copied
I posted the File Place Link Action manager code in my previous post. I do not know javascript I just hack at it. Looking at what you posted do not see a not found return indicator. So if not found I do not now what would be returned by your function. I do not know javascript.
Copy link to clipboard
Copied
I run your added code and i get. in the extendedScript tool:
General Photoshop error occurred. This function may not be available in this version of Photoshop.
well, of course it is available...
Copy link to clipboard
Copied
Did you change the code to make it a function and pass a valid image file or create a "C:\LO2G5037.CR2"?
Copy link to clipboard
Copied
your code has a function:
function placeEvent(ID, null2, linked, horizontal, vertical) {
var descriptor = new ActionDescriptor();
var descriptor2 = new ActionDescriptor();
var descriptor3 = new ActionDescriptor();
descriptor.putInteger( stringIDToTypeID( "ID" ), ID );
descriptor.putPath( charIDToTypeID( "null" ), null2 );
descriptor.putBoolean( stringIDToTypeID( "linked" ), linked );
descriptor.putObject( stringIDToTypeID( "openAs" ), charIDToTypeID( "null" ), descriptor2 );
descriptor.putEnumerated( stringIDToTypeID( "freeTransformCenterState" ), stringIDToTypeID( "quadCenterState" ), stringIDToTypeID( "QCSAverage" ));
descriptor3.putUnitDouble( stringIDToTypeID( "horizontal" ), stringIDToTypeID( "percentUnit" ), horizontal );
descriptor3.putUnitDouble( stringIDToTypeID( "vertical" ), stringIDToTypeID( "percentUnit" ), vertical );
descriptor.putObject( stringIDToTypeID( "offset" ), stringIDToTypeID( "offset" ), descriptor3 );
executeAction( stringIDToTypeID( "placeEvent" ), descriptor, DialogModes.NO );
}
i used it as:
placeEvent(3, new File(foundFile), true, 0, 0);
i noticed that the found file path is of the sort D:\Pictures\Adobe\Work\...
to make sure this was not a path issue i tried with:
foundFile = "D:\\Pictures\Adobe\Work\...";
and also
foundFile = "D:\Pictures\Adobe\Work\...";
none is working and the erorr is not related to a file not found
Copy link to clipboard
Copied
"\" is Jacascripts escape chacter to get am "\" you need to code \\
foundFile = "D:\\Pictures\\Adobe\\Work\\...";
for D:\Pictures\Adobe\Work\...
Copy link to clipboard
Copied
in any case regarding the path to the file - should i not use the file i found earlier in my script?
Copy link to clipboard
Copied
In any case you may not need to use Place link or Delete the original smart object layer you may just need the convert the original layer from and embedded Smart object layer to a linked smart object layer.
Here I opened my CR2 file in Photoshop as a embedded Smart object layer then I placed in the same CR2 as a Linked smart object layer. You can see this from the two different icons in their layer in the layers palette. One is a link the other an embedded object. Right click in the layer pallets on the linked one you can see a menu item "Reveal in Explorer" and if I use it Windows explorer will open to the file. If I right Click one the embedded smart object layer that menu item is not there but the is a "Relink to File" menu Item. If I use it I can convert the embedded smart object layer to a linked smart object layer, So once you find your file all you need to do is convert the layer to a linked smart object layer. Here is what the scriptlistener recorded.
// =======================================================
var idplacedLayerRelinkToFile = stringIDToTypeID( "placedLayerRelinkToFile" );
var desc121 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
desc121.putPath( idnull, new File( "C:\\LO2G5037.CR2" ) );
executeAction( idplacedLayerRelinkToFile, desc121, DialogModes.NO );
Copy link to clipboard
Copied
The code I posted I did not check it was straight from the Scriptlistene Placing Linked a RAW CR2 file. I have have problems in the past using code done with a RAW File I found Using code Placing a jpeg works fine and It also has no problem placing a RAW file. I tested the function and it works on my windows machine.
placeImageLinked("C:\\LO2G5037.CR2") ;
function placeImageLinked(file) {
var idPlc = charIDToTypeID( "Plc " );
var desc48 = new ActionDescriptor();
var idIdnt = charIDToTypeID( "Idnt" );
desc48.putInteger( idIdnt, 7 );
var idnull = charIDToTypeID( "null" );
desc48.putPath( idnull, new File( file ) );
var idLnkd = charIDToTypeID( "Lnkd" );
desc48.putBoolean( idLnkd, true );
var idFTcs = charIDToTypeID( "FTcs" );
var idQCSt = charIDToTypeID( "QCSt" );
var idQcsa = charIDToTypeID( "Qcsa" );
desc48.putEnumerated( idFTcs, idQCSt, idQcsa );
var idOfst = charIDToTypeID( "Ofst" );
var desc49 = new ActionDescriptor();
var idHrzn = charIDToTypeID( "Hrzn" );
var idPrc = charIDToTypeID( "#Prc" );
desc49.putUnitDouble( idHrzn, idPrc, 0.000000 );
var idVrtc = charIDToTypeID( "Vrtc" );
var idPrc = charIDToTypeID( "#Prc" );
desc49.putUnitDouble( idVrtc, idPrc, 0.000000 );
var idOfst = charIDToTypeID( "Ofst" );
desc48.putObject( idOfst, idOfst, desc49 );
executeAction( idPlc, desc48, DialogModes.NO );
return app.activeDocument.activeLayer;
}
Copy link to clipboard
Copied
I have very little scripting knowledge/experience, so I am just throwing out these ideas that may help if you can work out how to code them:
* When opening the Lightroom image as a smart object in Photoshop, the embedded image uses the source images filename without the extension as the layer name, perhaps this could be used in a filename mask?
* XMP “Ingredients File Path” metadata may have a full path including extension for the original image, perhaps this could be used as a default search path or a filename mask?
* XMP “Document ID” or “Original Document ID” or “Derived from Document ID” or “Derived from Original Document ID” metadata should match between the file opened from Lightroom and the image in Photoshop, which could possibly be leveraged to find the correct file.
Copy link to clipboard
Copied
i managed to get most of the script, there was a problem with the path and naming but now I am nearly there.
The problem is that i cannot place the linked file.
I always get an error of this sort:
My code is below:
and i call the place function here:
//placeEvent(3, new File(winPath), true, 0, 0);
placeLink(winPath);
function placeLink(fileName){
var d = new ActionDescriptor();
var d1 = new ActionDescriptor();
var d2 = new ActionDescriptor();
d.putInteger(stringIDToTypeID("ID"), 3);
d.putPath(stringIDToTypeID("null"), new File(fileName));
d.putBoolean(stringIDToTypeID("linked"), true);
d.putObject(stringIDToTypeID("openAs"), stringIDToTypeID("null"), d1);
d.putEnumerated(stringIDToTypeID("freeTransformCenterState"), stringIDToTypeID("quadCenterState"), stringIDToTypeID("QCSAverage"));
d2.putUnitDouble(stringIDToTypeID("horizontal"), stringIDToTypeID("pixelsUnit"), 0);
d2.putUnitDouble(stringIDToTypeID("vertical"), stringIDToTypeID("pixelsUnit"), 0);
d.putObject(stringIDToTypeID("offset"), stringIDToTypeID("offset"), d2);
executeAction(stringIDToTypeID("placeEvent"), d, DialogModes.NO);
}
function placeEvent(ID, null2, linked, horizontal, vertical) {
var descriptor = new ActionDescriptor();
var descriptor2 = new ActionDescriptor();
var descriptor3 = new ActionDescriptor();
descriptor.putInteger( stringIDToTypeID( "ID" ), ID );
descriptor.putPath( charIDToTypeID( "null" ), null2 );
descriptor.putBoolean( stringIDToTypeID( "linked" ), linked );
descriptor.putObject( stringIDToTypeID( "openAs" ), charIDToTypeID( "null" ), descriptor2 );
descriptor.putEnumerated( stringIDToTypeID( "freeTransformCenterState" ), stringIDToTypeID( "quadCenterState" ), stringIDToTypeID( "QCSAverage" ));
descriptor3.putUnitDouble( stringIDToTypeID( "horizontal" ), stringIDToTypeID( "percentUnit" ), horizontal );
descriptor3.putUnitDouble( stringIDToTypeID( "vertical" ), stringIDToTypeID( "percentUnit" ), vertical );
descriptor.putObject( stringIDToTypeID( "offset" ), stringIDToTypeID( "offset" ), descriptor3 );
executeAction( stringIDToTypeID( "placeEvent" ), descriptor, DialogModes.NO );
}
function modWinPath(path){
var newchar = '\\\\';
var winPath;
winPath = path.split('/').join(newchar);
winPath = winPath.replace("\\\\d\\\\", "d:\\\\");
winPath = winPath.replace("\\\\c\\\\", "c:\\\\");
return winPath;
}
var doc = app.activeDocument;
var fName = doc.name.split(".");
var fName = fName[0];
var layerN = doc.activeLayer.name;
var linkFolder = Folder.selectDialog("Selection prompt");
if(linkFolder !== null){
var linkFolderPath = linkFolder.path;
var linkName = linkFolder.name;
var linkFullPath = linkFolderPath+"/"+linkName;
function traverseFolder(path, layerN) {
var folder = new Folder(path);
suffix = new Array();
suffix[0] = "tif";
suffix[1] = "dng";
suffix[2] = "psd";
var files = folder.getFiles();
for (var i = 0; i < files.length; i++)
{
if (files instanceof File)
{
var fullPathName = Folder.decode(files);
var fileName = files.name;
fileName = decodeURI(fileName);
for(var s = 0; s < suffix.length ; s++){
search_name = layerN + "." + suffix;
search_NAME = layerN + "." + suffix.toUpperCase();
if ((fileName == search_name) || (fileName == search_NAME))
{
foundFile = fullPathName;
var winPath = modWinPath(foundFile);
alert("Try to place:\n" + winPath);
//placeEvent(3, new File(winPath), true, 0, 0);
placeLink(winPath);
}
}
}
else
{
traverseFolder(files, layerN);
}
}
}
traverseFolder(linkFullPath, layerN);
}
Copy link to clipboard
Copied
Another thing - even when i use the same code the event listener generated and try to run it, I get this error.
Copy link to clipboard
Copied
Replace
descriptor.putObject( stringIDToTypeID( "openAs" ), charIDToTypeID( "null" ), descriptor2 );
with
descriptor.putObject(stringIDToTypeID("openAs"), stringIDToTypeID("Adobe Camera Raw"), descriptor2 );
Copy link to clipboard
Copied
great!
it's working now
i'll tidy things up and place the code here.
do you know how i can select a layer below?
i need to delete the older smart object layer but it has the same name.
it is the layer below the new "active" link now.
so how do i select and delete it?
Copy link to clipboard
Copied
Delete previous layer.
var d = new ActionDescriptor();
var r = new ActionReference();
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("previous"));
d.putReference(stringIDToTypeID("null"), r);
executeAction(stringIDToTypeID("delete"), d, DialogModes.NO);
Copy link to clipboard
Copied
You could instead of placing in a new linked smart object later convert the original embedded smart object layer to a liknked one by using relink to file.
the scriptlistened code look like this
// =======================================================
var idplacedLayerRelinkToFile = stringIDToTypeID( "placedLayerRelinkToFile" );
var desc10 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
desc10.putPath( idnull, new File( "C:\\LO2G5037.CR2" ) );
executeAction( idplacedLayerRelinkToFile, desc10, DialogModes.NO );
IMO you want to do this from Lightroom. You should user Adobe feedback site and request a change to ACR the would add an additional option to open the image as a linked smart object layer that way a user can choose to open the image as a image background layer, or an embedded smarts object layer, or a linked smart object layer. If Adobe makes that change Adobe can add a option to Lightroom and have ACR open Files from Lightroon as Embedded or Linked smart object layer. Today Lightroom can only have ACR open file in Photoshop as embedded smart object layers.
Copy link to clipboard
Copied
Thanks but what you suggest forces me again to search for the file myself.
The script I created with all the help i got here help converting the smart object to linked.
It needs some more touch-ups, e,g. i can't return values from my recursion for if more than 1 file is found i will offer to place all.
Copy link to clipboard
Copied
i was looking for something similar as yours. I needed a script with which i could do bulk relinked. After find your script, the first iteration you posted, i started working on that and getting it to work properly. I noticed your variant had problems with the paths. I think if you just get the file path from the original array and use that it works flawless on all systems. I did not test my iteration on OSX yet though.
Anyways, with a combination of my own script parts and some of the layercomp exporter i got it work.
Its functionality now is that the user selects the layers containing the missing links. Then we prompt for a folder, and it runs over all. I also expanded it with some more file types for my needs.
This is really super nice, now i can work properly on my designs on both OSX and Windows without the need to manual relink all the files. Thats really hideous to do when you got around 100 or more linked files.
Ive also added some extra pars so it show in a sub part of the script menu
// Collection of scripts i gathered for quick relinken missing images
// Rombout Versluijs Dec 2020
/*
@@@BUILDINFO@@@ Restore Missing Links.jsx 0.0.0.7
*/
/*
// BEGIN__HARVEST_EXCEPTION_ZSTRING
/*
<javascriptresource>
<name>$$$/JavaScripts/RestoreMissingLinks/Menu=Restore Missing Links...</name>
<category>aaaThisPutsMeAtTheTopOfTheMenu</category>
//<category>Restore</category>
<enableinfo>true</enableinfo>
</javascriptresource>
// END__HARVEST_EXCEPTION_ZSTRING
*/
#target photoshop
// var docName = app.activeDocument.name;
// app.activeDocument = app.documents[docName];
docRef = app.activeDocument;
//var new_path = "D:\\_MINDFLOW\\CLIENTS\\Allusion\\Design\\Recourses\\Icons-24x"; // YOUR REAL NEW PATH
//alert(new_path)
function cTID(s) { return app.charIDToTypeID(s); };
function sTID(s) { return app.stringIDToTypeID(s); };
///////////////////////////////////////////////////
// Get Layer Kind
// https://www.ps-scripts.com/viewtopic.php?p=43242#p43242
// Get return layerkind by layerid
///////////////////////////////////////////////////
function getLayerKindByIndex(index) {
var ref, desc, adjustmentDesc, layerSectionType;
ref = new ActionReference();
ref.putIndex(charIDToTypeID("Lyr "), index);
desc = executeActionGet(ref);
var layerType = typeIDToStringID(desc.getEnumerationValue(stringIDToTypeID('layerSection')));
if (layerType != 'layerSectionContent') return; // return if layerSet
if (desc.hasKey(stringIDToTypeID('textKey'))) return LayerKind.TEXT;
if (desc.hasKey(stringIDToTypeID('smartObject'))) return LayerKind.SMARTOBJECT; // includes LayerKind.VIDEO
if (desc.hasKey(stringIDToTypeID('layer3D'))) return LayerKind.LAYER3D;
if (desc.hasKey(stringIDToTypeID('videoLayer'))) return LayerKind.VIDEO;
if (desc.hasKey(stringIDToTypeID('adjustment'))) {
switch (typeIDToStringID(desc.getList(stringIDToTypeID('adjustment')).getClass(0))) {
case 'photoFilter':
return LayerKind.PHOTOFILTER;
case 'solidColorLayer':
return LayerKind.SOLIDFILL;
case 'gradientMapClass':
return LayerKind.GRADIENTMAP;
case 'gradientMapLayer':
return LayerKind.GRADIENTFILL;
case 'hueSaturation':
return LayerKind.HUESATURATION;
case 'colorLookup':
return udefined; //this does not exist and errors with getting layer kind
case 'colorBalance':
return LayerKind.COLORBALANCE;
case 'patternLayer':
return LayerKind.PATTERNFILL;
case 'invert':
return LayerKind.INVERSION;
case 'posterization':
return LayerKind.POSTERIZE;
case 'thresholdClassEvent':
return LayerKind.THRESHOLD;
case 'blackAndWhite':
return LayerKind.BLACKANDWHITE;
case 'selectiveColor':
return LayerKind.SELECTIVECOLOR;
case 'vibrance':
return LayerKind.VIBRANCE;
case 'brightnessEvent':
return LayerKind.BRIGHTNESSCONTRAST;
case 'channelMixer':
return LayerKind.CHANNELMIXER;
case 'curves':
return LayerKind.CURVES;
case 'exposure':
return LayerKind.EXPOSURE;
// if not one of the above adjustments return - adjustment layer type
default:
return typeIDToStringID(desc.getList(stringIDToTypeID('adjustment')).getClass(0));
}
}
return LayerKind.NORMAL; // if we get here normal should be the only choice left.
};
/// ////////////////////////////////////////////////////////////////////////////
// Function: Combination of applyToAllLayersAMIdx & getLayerInfo
// Usage: extract a list of index values of all the selected layers & ID layernamer
// Input:: (active document.) s
// Return: array of indexes ID"s of selected layers.
// Mixed this so it only loops once over all layers
/// ////////////////////////////////////////////////////////////////////////////
function applyToAllLayersInfo(docRef) {
// alert(docRef)
var selectedLayers = new Array()
var ref = new ActionReference()
// get a number list of selected artLayers in the document
ref.putProperty(app.charIDToTypeID("Prpr"), stringIDToTypeID("targetLayers"))
ref.putEnumerated(charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt"))
// what do I want to do this this list? Define an description of an action.
var desc = executeActionGet(ref)
// if the selected object has the "Target Layers" key (only works CS4+)
if (desc.hasKey(stringIDToTypeID("targetLayers"))) {
desc = desc.getList(stringIDToTypeID("targetLayers"))
var c = desc.count
var selectedLayers = [] // for each
for (var i = 0; i < c; i++) {
var lyr = {}
var lyrIndex;
try {
docRef.backgroundLayer // try to select a background layer, if I can then adjust the index counting. (Background layers change index counitng of all layers by 1)
// selectedLayers.push(desc.getReference(i).getIndex())
lyrIndex = desc.getReference(i).getIndex();
} catch (e) {
// selectedLayers.push(desc.getReference(i).getIndex() + 1)
lyrIndex = desc.getReference(i).getIndex() + 1;
}
// Added from getLayerInfo
var lyrRef = new ActionReference();
lyrRef.putIndex(charIDToTypeID("Lyr "), lyrIndex)
var lyrDesc = executeActionGet(lyrRef);
// alert(lyrDesc.getInteger(stringIDToTypeID("layerKind")))
var Id = lyrDesc.getInteger(stringIDToTypeID("layerID"));
lyr.AMid = Id;
lyr.lyrKind = getLayerKindByIndex(lyrIndex);
lyr.lyrIndex = lyrIndex;
selectedLayers.push(lyr);
}
}
return selectedLayers
}
///////////////////////////////////////////////////
// Select Layer by LayerIndex
// Source: https://stackoverflow.com/questions/26295492/photoshop-script-new-layer-below-current-layer
// select [LayerNum], optionally [add] to selection (if add=2: with inclusion)
///////////////////////////////////////////////////
function selLyr(LyrN, add) {
var adesc = new ActionDescriptor();
var aref = new ActionReference();
aref.putIndex(charIDToTypeID("Lyr "), LyrN);
adesc.putReference(charIDToTypeID("null"), aref);
if (add) {
add = (add == 2) ? stringIDToTypeID("addToSelectionContinuous") : stringIDToTypeID("addToSelection");
adesc.putEnumerated(stringIDToTypeID("selectionModifier"), stringIDToTypeID("selectionModifierType"), add);
}
adesc.putBoolean(charIDToTypeID("MkVs"), false);
return executeAction(charIDToTypeID("slct"), adesc, DialogModes.NO);
}
///////////////////////////////////////////////////
// Select Layers by LayerID
// Source: https://graphicdesign.stackexchange.com/questions/130739/photoshop-scripting-applying-changes-only-to-selected-artboards
///////////////////////////////////////////////////
function selectById(AMid) {
var desc1 = new ActionDescriptor();
var ref1 = new ActionReference();
ref1.putIdentifier(charIDToTypeID('Lyr '), AMid);
desc1.putReference(charIDToTypeID('null'), ref1);
executeAction(charIDToTypeID('slct'), desc1, DialogModes.NO);
}
/// ////////////////////////////////////////////////////////////////////////////
// Function: getSelectedLayersAMIdx
// Usage: extract a list of index values of all the selected layers.
// Input:: (active document.) s
// Return: array of indexes ID"s of selected layers.
/// ////////////////////////////////////////////////////////////////////////////
function getSelectedLayersAMIdx(docRef) {
var selectedLayers = new Array()
var ref = new ActionReference()
// get a number list of selected artLayers in the document
ref.putProperty(app.charIDToTypeID("Prpr"), stringIDToTypeID("targetLayers"))
ref.putEnumerated(charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt"))
// what do I want to do this this list? Define an description of an action.
var desc = executeActionGet(ref)
// if the selected object has the "Target Layers" key (only works CS4+)
if (desc.hasKey(stringIDToTypeID("targetLayers"))) {
desc = desc.getList(stringIDToTypeID("targetLayers"))
var c = desc.count
var selectedLayers = [] // for each
for (var i = 0; i < c; i++) {
try {
docRef.backgroundLayer // try to select a background layer, if I can then adjust the index counting. (Background layers change index counitng of all layers by 1)
selectedLayers.push(desc.getReference(i).getIndex())
} catch (e) {
selectedLayers.push(desc.getReference(i).getIndex() + 1)
}
}
}
return selectedLayers
}
/// ////////////////////////////////////////////////////////////////////////////
// Function: relinkFiles
// Usage: relinks path to missing linked image.
// Input:: string of path to found image
// Return: correct linked image
// source: https://community.adobe.com/t5/photoshop/update-linked-smart-objects-broken-file-path/m-p/11707888?page=1#M213131
/// ////////////////////////////////////////////////////////////////////////////
function relinkFiles() {
var r = new ActionReference();
r.putProperty(stringIDToTypeID("property"), stringIDToTypeID("smartObject"));
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
var d = executeActionGet(r);
if (d.hasKey(stringIDToTypeID("smartObject"))) {
d = d.getObjectValue(stringIDToTypeID("smartObject"));
if (d.hasKey(stringIDToTypeID("link"))) {
var type = d.getType(stringIDToTypeID("link"));
var pth;
switch (type) {
case DescValueType.ALIASTYPE:
pth = d.getPath(stringIDToTypeID("link"));
break;
case DescValueType.STRINGTYPE:
pth = d.getString(stringIDToTypeID("link"));
break;
default:
throw ("\n\nHmmm!\n\n");
}
var file = new File(pth);
if (!file.exists) {
file = new File(new_path + "\\" + file.name);
var d = new ActionDescriptor();
d.putPath(stringIDToTypeID("null"), file);
//executeAction(stringIDToTypeID("placedLayerRelinkToFile"), d, DialogModes.NO);// replace link
executeAction(stringIDToTypeID("placedLayerReplaceContents"), d, DialogModes.NO); // replace content
alert("Done!")
} else
alert("File already exists");
} else {
alert("No link");
}
} else {
alert("No smartObject");
}
}
/// ////////////////////////////////////////////////////////////////////////////
// Function: searchFolder
// Usage: prompt for folder and loop over files
// Input:: folder path
// Return: correct linked image
// source: https://community.adobe.com/t5/photoshop/place-linked-using-script/td-p/10205968?page=1
/// ////////////////////////////////////////////////////////////////////////////
var linkFolder = Folder.selectDialog("Selection prompt");
// alert(linkFolder)
function searchFolder(){
var layerN = docRef.activeLayer.name;
if(linkFolder !== null){
var linkFolderPath = linkFolder.path;
var linkName = linkFolder.name;
var linkFullPath = linkFolderPath+"/"+linkName;
function traverseFolder(path, layerN) {
var folder = new Folder(path);
suffix = new Array();
suffix[0] = "tif";
suffix[1] = "dng";
suffix[2] = "psd";
suffix[3] = "svg";
suffix[4] = "jpg";
var files = folder.getFiles();
for (var i = 0; i < files.length; i++) {
if (files[i] instanceof File) {
var fullPathName = Folder.decode(files[i]);
//alert(fullPathName)
var fileName = files[i].name;
//alert(fileName)
fileName = decodeURI(fileName);
//alert(fileName)
for(var s = 0; s < suffix.length ; s++){
search_name = layerN + "." + suffix[s];
search_NAME = layerN + "." + suffix[s].toUpperCase();
if ((fileName == search_name) || (fileName == search_NAME))
{
foundFile = fullPathName + "/" + fileName;
//alert("Found: " + foundFile);
//alert("Found: " + files[i]);
relinkFile(files[i])
return files[i] ;
}
}
} else {
traverseFolder(files[i], layerN);
// return traverseFolder(files, layerN);
}
}
}
function relinkFile(foundFile){
var d = new ActionDescriptor();
d.putPath( cTID('null'), new File( foundFile ) );
executeAction(stringIDToTypeID("placedLayerRelinkToFile"), d, DialogModes.NO);// replace link
// executeAction( sTID('placedLayerReplaceContents'), d, DialogModes.NO );// replace content
}
var foundFile = traverseFolder(linkFullPath, layerN);
//alert(foundFile)
}
}
var layerInfo = applyToAllLayersInfo(docRef);
for (var i = 0; i < layerInfo.length; i++) {
selectById(layerInfo[i].AMid);
searchFolder()
}