How to parse subfolders for find specific files? - photoshop

I Would like to create a script for photoshop who allow me to search some files with a specific name (for example : 300x250_F1.jpg, 300x250_F2.jpg, 300x600_F1.jpg, etc... ) in differents subfolders (all in the same parent folder) and after load them in my active document. The problem is names of subfolders will be everytime differents.
I definitely need some help :)

i found a code which almost do what i want (thank you).
I'm almost good but i have a problem: if the variable "mask" have only one value, it works. But with few values, it doesn't work anymore.
I think it's because i made an array with the mask variable and i have to update the script...
var topFolder = Folder.selectDialog("");
//var topFolder = new Folder('~/Desktop/PS_TEST');
var fileandfolderAr = scanSubFolders(topFolder, /\.(jpg)$/i);
//var fileandfolderAr = scanSubFolders(topFolder, /\.(jpg|tif|psd|bmp|gif|png|)$/i);
var fileList = fileandfolderAr[0];
var nom = decodeURI(fileList);
//all file paths found and amount of files found
//alert("fileList: " + nom + "\n\nFile Amount: " + fileList.length);
//alert(allFiles);
for (var a = 0; a < fileList.length; a++) {
var docRef = open(fileList[a]);
//do things here
}
function scanSubFolders(tFolder, mask) { // folder object, RegExp or string
var sFolders = [];
var allFiles = [];
var mask = ["300x250_F1", "300x250_F2"];
sFolders[0] = tFolder;
for (var j = 0; j < sFolders.length; j++) { // loop through folders
var procFiles = sFolders[j].getFiles();
for (var i = 0; i < procFiles.length; i++) { // loop through this folder contents
if (procFiles[i] instanceof File) {
if (mask == undefined) {
allFiles.push(procFiles); // if no search mask collect all files
}
if (procFiles[i].fullName.search(mask) != -1) {
allFiles.push(procFiles[i]); // otherwise only those that match mask
}
}
else if (procFiles[i] instanceof Folder) {
sFolders.push(procFiles[i]); // store the subfolder
scanSubFolders(procFiles[i], mask); // search the subfolder
}
}
}
return [allFiles, sFolders];
}

There are several ways you can accomplish this without reassigning mask within the scanSubFolders function.
Solution 1: use a regex
The function is already set up to accept a regex or string as a mask. You just need to use one that would match the pattern of the files you're targeting.
var fileandfolderAr = scanSubFolders(topFolder, /300x250_F(1|2)/gi);
Solution 2: call the function within a loop
If regex isn't your thing, you could still utilize an array of strings, but do it outside the function. Loop the array of masks and call the function with each one, then execute your primary logic on the results of each call.
var topFolder = Folder.selectDialog("");
var myMasks = ["300x250_F1", "300x250_F2"];
for (var index in myMasks) {
var mask = myMasks[index]
var fileandfolderAr = scanSubFolders(topFolder, mask);
var fileList = fileandfolderAr[0];
for (var a = 0; a < fileList.length; a++) {
var docRef = open(fileList[a]);
//do things here
}
}
Don't forget to remove var mask = ["300x250_F1", "300x250_F2"]; from within the scanSubFolders function or else these won't work.

Related

Photoshop Scripting: Relink Smart Object

I'm working on a script that should go through a photoshop document and relink all visible linked objects to a new specified file. I've gotten the loop working so that it cycles through every layer and collects only the visible layers, but for the life of me I can't find if there's a method available to relink a smart object. The closest I've found is this script:
https://gist.github.com/laryn/0a1f6bf0dab5b713395a835f9bfa805c
but when it gets to desc3.putPath(idnull, new File(newFile));, it spits out an error indicating that the functionality may not be present in the current Photoshop version. The script itself is 4 years old so it may be out of date.
Any help would be appreciated!
MY script as it stands is below:
// SELECT FILE //
var files = File.openDialog("Please select new linked file");
var selectedFile = files[0];
// GET ALL LAYERS //
var doc = app.activeDocument;
var allLayers = [];
var allLayers = collectAllLayers(doc, allLayers);
function collectAllLayers (doc, allLayers)
{
for (var m = 0; m < doc.layers.length; m++)
{
var theLayer = doc.layers[m];
if (theLayer.typename === "ArtLayer")
{
allLayers.push(theLayer);
}
else
{
collectAllLayers(theLayer, allLayers);
}
}
return allLayers;
}
// GET VISIBLE LAYERS //
var visibleLayers = [];
for (i = 0; i < allLayers.length; i++)
{
var layer = allLayers[i];
if (layer.visible && layer.kind == LayerKind.SMARTOBJECT)
{
visibleLayers.push(layer);
}
}
// REPLACE LAYERS
for (i = 0; i < visibleLayers.length; i++)
{
var layer = visibleLayers[i];
//--> REPLACE THE FILE HERE
}
Note: I am aware that this script currently may be error-prone if you don't know exactly how it works; I'm not intending to publish it at this time so I'm not super concerned with that at the moment. Mostly I just need the core functionality to work.
I used an AM function for getting visible smart objects — it works much faster. But if you want you can use yours. The important bit is relinkSO(path);: it'll also work in your script (just don't forget to select a layer: activeDocument.activeLayer = visibleLayers[i];)
Note that it works similar to Photoshop Relink to File command — if used on one instance of Smart Object all the instances are going to be relinked. If you want to relink only specific layers you'll have to break instancing first (probably using the New Smart Object via Copy command)
function main() {
var myFile = Folder.myDocuments.openDlg('Load file', undefined, false);
if (myFile == null) return false;
// gets IDs of all smart objects
var lyrs = getLyrs();
for (var i = 0; i < lyrs.length; i++) {
// for each SO id...
// select it
selectById(lyrs[i]);
// relink SO to file
relinkSO(myFile);
// embed linked if you want
embedLinked()
}
function getLyrs() {
var ids = [];
var layers, desc, vis, type, id;
try
{
activeDocument.backgroundLayer;
layers = 0;
}
catch (e)
{
layers = 1;
}
while (true)
{
ref = new ActionReference();
ref.putIndex(charIDToTypeID('Lyr '), layers);
try
{
desc = executeActionGet(ref);
}
catch (err)
{
break;
}
vis = desc.getBoolean(charIDToTypeID("Vsbl"));
type = desc.getInteger(stringIDToTypeID("layerKind"));
id = desc.getInteger(stringIDToTypeID("layerID"));
if (type == 5 && vis) ids.push(id);
layers++;
}
return ids;
} // end of getLyrs()
function selectById(id) {
var desc = new ActionDescriptor();
var ref = new ActionReference();
ref.putIdentifier(charIDToTypeID('Lyr '), id);
desc.putReference(charIDToTypeID('null'), ref);
executeAction(charIDToTypeID('slct'), desc, DialogModes.NO);
} // end of selectById()
function relinkSO(path) {
var desc = new ActionDescriptor();
desc.putPath( charIDToTypeID('null'), new File( path ) );
executeAction( stringIDToTypeID('placedLayerRelinkToFile'), desc, DialogModes.NO );
} // end of relinkSO()
function embedLinked() {
executeAction( stringIDToTypeID('placedLayerConvertToEmbedded'), undefined, DialogModes.NO );
} // end of embedLinked()
}
app.activeDocument.suspendHistory("relink SOs", "main()");

Run last Photoshop script (again)

This seems like a trivial issue but I'm not sure Photoshop supports this type of functionality:
Is it possible to implement use last script functionality?
That is without having to add a function on each and every script that writes it's filename to a text file.
Well... It's a bit klunky, but I suppose you could read in the scriptlistener in reverse order and find the first mention of a script file:
// Switch off any dialog boxes
displayDialogs = DialogModes.NO; // OFF
var scripts_folder = "D:\\PS_scripts";
var js = "C:\\Users\\GhoulFool\\Desktop\\ScriptingListenerJS.log";
var jsLog = read_file(js);
var lastScript = process_file(jsLog);
// use function to call scripts
callScript(lastScript)
// Set Display Dialogs back to normal
displayDialogs = DialogModes.ALL; // NORMAL
function callScript (ascript)
{
eval('//#include "' + ascript + '";\r');
}
function process_file(afile)
{
var needle = ".jsx";
var msg = "";
// Let's do this backwards
for (var i = afile.length-1; i>=0; i--)
{
var str = afile[i];
if(str.indexOf(needle) > 0)
{
var regEx = str.replace(/(.+new\sFile\(\s")(.+\.jsx)(.+)/gim, "$2");
if (regEx != null)
{
return regEx;
}
}
}
}
function read_file(inFile)
{
var theFile = new File(inFile);
//read in file
var lines = new Array();
var l = 0;
var txtFile = new File(theFile);
txtFile.open('r');
var str = "";
while(!txtFile.eof)
{
var line = txtFile.readln();
if (line != null && line.length >0)
{
lines[l++] = line;
}
}
txtFile.close();
return lines;
}

Replacing smart objects in bulk with Photoshop

Just facing this issue: I have a mockup in Photoshop with two smart-objects: Rectangle 14.psb and Place your logo.psb
I have 100+ images in png that should be applied to create mockups.
For this reason, I would like your help to create a script that:
Let me select the png file that I would like to use
Open the smart objects (Rectangle 14.psb and Place your logo.psb)
Re-Link the same png to the layers "place your logo" of both the smart objects.
Finally, the script should save the file as png with the same file name of the selected png file adding just _new after its name.
So far I have tried this code without any luck:
#target photoshop
if (app.documents.length > 0) {
var myDocument = app.activeDocument;
var theName = myDocument.name.match(/(.*)\.[^\.]+$/)[1];
var thePath = myDocument.path;
var theLayer = myDocument.activeLayer;
// PSD Options;
psdOpts = new PhotoshopSaveOptions();
psdOpts.embedColorProfile = true;
psdOpts.alphaChannels = true;
psdOpts.layers = true;
psdOpts.spotColors = true;
// Check if layer is SmartObject;
if (theLayer.kind != "LayerKind.SMARTOBJECT") {
alert("selected layer is not a smart object")
} else {
// Select Files;
if ($.os.search(/windows/i) != -1) {
var theFiles = File.openDialog("please select files",
"*.psd;*.tif;*.jpg;*.png", true)
} else {
var theFiles = File.openDialog("please select files", getFiles,
true)
};
if (theFiles) {
for (var m = 0; m < theFiles.length; m++) {
// Replace SmartObject
theLayer = replaceContents(theFiles[m], theLayer);
var theNewName = theFiles[m].name.match(/(.*)\.[^\.]+$/)[1];
// Save JPG
myDocument.saveAs((new File(thePath + "/" + theName + "_" +
theNewName + ".psd")), psdOpts, true);
}
}
}
};
// Get PSDs, TIFs and JPGs from files
function getFiles(theFile) {
if (theFile.name.match(/\.(psd|png|jpg)$/i) != null ||
theFile.constructor.name == "Folder") {
return true
};
};
// Replace SmartObject Contents
function replaceContents(newFile, theSO) {
app.activeDocument.activeLayer = theSO;
// =======================================================
var idplacedLayerReplaceContents =
stringIDToTypeID("placedLayerReplaceContents");
var desc3 = new ActionDescriptor();
var idnull = charIDToTypeID("null");
desc3.putPath(idnull, new File(newFile));
var idPgNm = charIDToTypeID("PgNm");
desc3.putInteger(idPgNm, 1);
executeAction(idplacedLayerReplaceContents, desc3, DialogModes.NO);
return app.activeDocument.activeLayer
};
The above code substitute the smart object but I would like just to re-link the layer withing the smartobject to a new image and save the file. Any help would be much appreciated!
Are you familiar with Scriptlistener? You can use it to get all the functions you need and then modify the output to run within your loop of 100 pngs, it should be straightforward.

Google Script - Attachments to Drive

I have the code below, but i've been struggling to get it to work properly, either it creates duplicate folders every time i run it, or it doesn't upload the attachments and just creates the folders... I am also getting an error now that the newMail Uploads object does not have a .hasnext() function.
What i want to do, is have this script running and it puts attachments in a folder relating to their label -- So in the code below, all mail with the newMail label would go to a single folder, but i want to be able to extend the code further to run for multiple labels ect, so i want to check if the relevant folders exist and if not create them, if they exist they should be used.
-Edit, Now it is only taking the attachment from the first email from a certain address.
function startProcess()
{
var gmailLabels = "newLabel";
var driveFolder = "newFolder";
var archiveLabel = "Processed";
var moveToLabel = GmailApp.getUserLabelByName(archiveLabel);
if ( ! moveToLabel )
{
moveToLabel = GmailApp.createLabel(archiveLabel);
}
findFolder(gmailLabels, driveFolder, archiveLabel, moveToLabel);
}
function findFolder(gmailLabels, driveFolder, archiveLabel, moveToLabel)
{
var filter = "has:attachment label:" + gmailLabels;
var folder = DriveApp.getFoldersByName(driveFolder);
if (folder.hasNext()) {
folder = folder.next();
} else {
folder = DriveApp.createFolder(driveFolder);
}
callThreads(gmailLabels, driveFolder, archiveLabel, moveToLabel, filter, folder)
}
function callThreads(gmailLabels, driveFolder, archiveLabel, moveToLabel, filter, folder)
{
var threads = GmailApp.search(filter);
for (var x=0; x<threads.length; x++) {
var label = GmailApp.getUserLabelByName(gmailLabels);
var message = threads[x].getMessages()[x];
var desc = message.getSubject() + " #" + message.getId();
var att = message.getAttachments();
for (var z=0; z<att.length; z++) {
try {
file = folder.createFile(att[z]);
file.setDescription(desc);
}
catch (e) {
Logger.log(e.toString());
}
}
//threads[x].addLabel(moveToLabel);
label.removeFromThreads(threads);
//threads[x].moveToTrash();
}
}
This is just a suggestion. I haven't tested this code, it probably doesn't work. But that's not the point. I'm trying to show how you might create some order to your code so that it's easier to understand and debug:
function sendToGoogleDrive() {
makeNewFolders();
putEmailsIntoFolders();
};
function makeNewFolders() {
var gmailLabels = "newMail";
var driveFolder = "newMail";
var archiveLabel = "Processed";
var rootUploadFolders ="newMail Uploads";
var rootDriveFolder = DriveApp.getFolders();
var rootExist = false;
var childExist = false;
while (rootDriveFolder.hasNext()) {
var folder = rootDriveFolder.next();
if(folder.getName()==rootUploadFolders) {
var folderId = folder.getId();
if(folder.getName()!==driveFolder) {
var child = DriveApp.getFolderById(folderId).createFolder(driveFolder);
};
};
};
};
function putEmailsIntoFolders() {
};

How do I programmatically capture and replicate Ai path shape?

I'm using ExtendScript for scripting Adobe Illustrator. I was wondering if there was a sneaky way or a script available to programmatically capture and then replicate a path shape, sort of JavaScript's .toSource() equivalent.
Thanks
Try this:
main();
function main(){
var doc = app.activeDocument; // get the active doc
var coords = new Array(); // make a new array for the coords of the path
var directions = new Array();
var sel = doc.selection[0];// get first object in selection
if(sel == null) {
// check if something is slected
alert ("You need to sevlect a path");
return;
}
var points = sel.pathPoints;// isolate pathpoints
// loop points
for (var i = 0; i < points.length; i++) {
// this could be done in one lines
// just to see whats going on line like
//~ coords.push(new Array(points[i].anchor[0],points[i].anchor[1]));
var p = points[i]; // the point
var a = p.anchor; // his anchor
var px = a[0];// x
var py = a[1]; // y
var ldir = p.leftDirection;
var rdir = p.rightDirection;
directions.push(new Array(ldir,rdir));
coords.push(new Array(px,py));// push into new array of array
}
var new_path = doc.pathItems.add(); // add a new pathitem
new_path.setEntirePath(coords);// now build the path
// check if path was closed
if(sel.closed){
new_path.closed = true;
}
// set the left and right directions
for(var j = 0; j < new_path.pathPoints.length;j++){
new_path.pathPoints[j].leftDirection = directions[j][0];
new_path.pathPoints[j].rightDirection = directions[j][1];
}
}