Copy link to clipboard
Copied
I saved all my Photoshop linked-Files of my multiple Photoshop Data in a folder. I moved the folder which contains the linked-files to another place.
Now whenever I open a file, I need to relink it and it's not so bad. But... I have lots of photoshop files which their liked-files are moved to another place now (they are all in the same folder as before) and I need to open every single file and relink it again.
Added saving the previously selected path. Try it:
#target photoshop
/*
// BEGIN__HARVEST_EXCEPTION_ZSTRING
<javascriptresource>
<name>Relink files</name>
<category>jazzy</category>
<enableinfo>true</enableinfo>
</javascriptresource>
// END__HARVEST_EXCEPTION_ZSTRING
*/
$.localize = true
const UUID = 'a3e4d053-135c-4225-b741-39bcb6656fd0',
apl = new AM('application'),
doc = new AM('document'),
lr = new AM('layer'),
fn = new CommonFunctions(),
str = new Locale(),
ver =
...
Copy link to clipboard
Copied
Hi! A similar question was discussed in the topic Is there an option that shows me a list with all smart objects paths?
I planned to finish this script and add the ability to work in batch mode with predefined parameters, but so far I do not have time for this task š
Copy link to clipboard
Copied
Perfect, but can I use it now as a script for my Problem? (relink file in mupltiple Photoshop Files at once)
Copy link to clipboard
Copied
Nobody is keeping you from creating a Script that meets your needs.
What is giving you problems?
What is the exact scenario? Operate on open files or select files via dialog, relink all linked Smart Objects, only missing linked Smart Objects, what is the exact Folder-structure, �
Copy link to clipboard
Copied
I had my old structure file order:
1- Photoshop File
2- Photoshop linked-Files
3- My Old Files-Structure / Order
I moved just the photosohp linked files in my (newData) folder:
Now whenever I open a photoshop file, it askes me to relink the missing file. (in every Photoshop file, I have just one linked file)
I have lot of file which now thier linked files are missing. I dont want to open every file and relink its missing link.
Copy link to clipboard
Copied
The main problem is that when opening each file with broken links, Photoshop shows a modal window that stops the script. This completely destroys the ability to batch process multiple files without user interaction.
Copy link to clipboard
Copied
I apologize then; I was mistaken and there is something that does make the creation of such a Script impossible (or hopefully just difficult?).
Copy link to clipboard
Copied
may be creating an action with/without Script to open a file and automaticaly change the link address? Is it possible?
Copy link to clipboard
Copied
Nothing is impossible.
I see four options:
Since working with broken links is more an exception than a rule for my workflow, I planned to simply throw parameters to the script via an action (for working with a standard batch) and manually close notification windows ĀÆ\_(ć)_/ĀÆ
Copy link to clipboard
Copied
I tried your first option. It works like a charm š
but with a (pretty much) long delay š
Copy link to clipboard
Copied
Thanks a lot. I dont have to search for the one single file in a bunch of files š
Copy link to clipboard
Copied
Try turning this option off (unless you're using links inside embedded smart objects). This will speed up the document pre-processing time.
Copy link to clipboard
Copied
I've allready tried. but the some reasult.
And also if I triger the script using action, it opens the "Linked files" Dialog and I need to change the sittings again.
Can I set the Target Path in a constaint Path as default so I dont have to select open Folder... and brouse the file?
Copy link to clipboard
Copied
Added saving the previously selected path. Try it:
#target photoshop
/*
// BEGIN__HARVEST_EXCEPTION_ZSTRING
<javascriptresource>
<name>Relink files</name>
<category>jazzy</category>
<enableinfo>true</enableinfo>
</javascriptresource>
// END__HARVEST_EXCEPTION_ZSTRING
*/
$.localize = true
const UUID = 'a3e4d053-135c-4225-b741-39bcb6656fd0',
apl = new AM('application'),
doc = new AM('document'),
lr = new AM('layer'),
fn = new CommonFunctions(),
str = new Locale(),
ver = '0.23';
var allowedExtensions = function (s) { var t = {}; for (var i = 0; i < s.length; i++) t[s[i]] = true; return t }(['PSD', 'PDD', 'PSDT', 'PSB', 'BMP', 'RLE', 'DIB', 'GIF', 'EPS', 'IFF', 'TDI', 'JPG', 'JPEG', 'JPE', 'JPF', 'JPX', 'JP2', 'J2C',
'J2K', 'JPC', 'JPS', 'MPO', 'PCX', 'PDF', 'PDP', 'PXR', 'PNG', 'SCT', 'TGA', 'VDA', 'ICB', 'VST', 'TIFF', 'PBM', 'PGM', 'PPM', 'PNM', 'PFM', 'PAM',
'DCM', 'DC3', 'DIC', 'TIF', 'CRW', 'NEF', 'RAF', 'ORF', 'MRW', 'MOS', 'SRF', 'PEF', 'DCR', 'CR2', 'DNG', 'ERF', 'X3F', 'RAW', 'ARW', 'CR3', 'KDC', '3FR',
'MEF', 'MFW', 'NRW', 'RWL', 'RW2', 'SRW', 'GPR', 'IIQ']),
cfg = (new AM()).getScriptSettings();
main();
function main() {
try {
var runMode = version.split('.')[0] < 21 ? false : true;
if (!runMode) cfg.checkEmbedded = false;
do {
var currentSelection = fn.getSelectedLayersIds(),
smartObjects = fn.buildSmartObjectsTree(),
filesList = fn.buildFilesList(smartObjects);
if (fn.isDitry) app.refresh()
var w = new mainWindow(filesList, runMode),
result = w.show();
if (result == 1) {
activeDocument.suspendHistory(str.relink.toString(), 'fn.relink(filesList, smartObjects)')
if (currentSelection.length) doc.selectLayerByIDList(currentSelection)
}
fn.isDitry = false
} while (result == 3)
} catch (e) {
if (confirm(str.errGlobal, true, str.err)) fn.createReport(smartObjects, filesList, e);
}
}
function AM(target) {
var s2t = stringIDToTypeID,
t2s = typeIDToStringID;
target = target ? s2t(target) : null;
this.getProperty = function (property, id, idxMode) {
r = new ActionReference();
if (property) {
property = s2t(property);
r.putProperty(s2t('property'), property);
}
id != undefined ? (idxMode ? r.putIndex(target, id) : r.putIdentifier(target, id)) :
r.putEnumerated(target, s2t('ordinal'), s2t('targetEnum'));
try { return property ? getDescValue(executeActionGet(r), property) : executeActionGet(r) } catch (e) { return null }
}
this.hasProperty = function (property, id, idxMode) {
property = s2t(property);
(r = new ActionReference()).putProperty(s2t('property'), property);
id ? (idxMode ? r.putIndex(target, id) : r.putIdentifier(target, id))
: r.putEnumerated(target, s2t('ordinal'), s2t('targetEnum'));
try { return executeActionGet(r).hasKey(property) } catch (e) { return false }
}
this.descToObject = function (d, o) {
if (d) {
o = o ? o : {}
for (var i = 0; i < d.count; i++) {
var k = d.getKey(i)
o[t2s(k)] = getDescValue(d, k)
}
return o
}
}
this.relinkCurrentLayer = function (pth) {
try {
(d = new ActionDescriptor()).putPath(s2t('target'), pth);
executeAction(s2t('placedLayerRelinkToFile'), d, DialogModes.NO);
return true;
} catch (e) { return false }
}
this.deleteCurrentHistoryState = function () {
(r = new ActionReference()).putProperty(s2t('historyState'), s2t('currentHistoryState'));
(d = new ActionDescriptor()).putReference(s2t('target'), r);
try { executeAction(s2t('delete'), d, DialogModes.NO); } catch (e) { }
}
this.convertSmartObjectToLayers = function () {
try { executeAction(s2t('placedLayerConvertToLayers'), undefined, DialogModes.NO); return true } catch (e) { return false }
}
this.selectLayerByIDList = function (IDList) {
var ref = new ActionReference()
for (var i = 0; i < IDList.length; i++) {
ref.putIdentifier(s2t('layer'), IDList[i])
}
var desc = new ActionDescriptor()
desc.putReference(s2t('target'), ref)
desc.putBoolean(s2t('makeVisible'), false)
executeAction(s2t('select'), desc, DialogModes.NO)
}
this.editSmartObject = function () {
try {
executeAction(s2t('placedLayerEditContents'), undefined, DialogModes.NO)
return true
} catch (e) { return false }
}
this.closeDocument = function (save) {
save = save != true ? s2t('no') : s2t('yes');
(d = new ActionDescriptor()).putEnumerated(s2t('saving'), s2t('yesNo'), save);
executeAction(s2t('close'), d, DialogModes.NO)
}
this.applyLocking = function (desc, id, idxMode) {
if (!desc) {
desc = new ActionDescriptor();
desc.putBoolean(s2t('protectNone'), true);
}
var r = new ActionReference();
id ? (idxMode ? r.putIndex(target, id) : r.putIdentifier(target, id))
: r.putEnumerated(target, s2t('ordinal'), s2t('targetEnum'));
(d = new ActionDescriptor()).putReference(s2t('null'), r);
d.putObject(s2t('layerLocking'), s2t('layerLocking'), desc);
executeAction(s2t('applyLocking'), d, DialogModes.NO);
}
function getDescValue(d, p) {
switch (d.getType(p)) {
case DescValueType.OBJECTTYPE: return { type: t2s(d.getObjectType(p)), value: d.getObjectValue(p) };
case DescValueType.LISTTYPE: return d.getList(p);
case DescValueType.REFERENCETYPE: return d.getReference(p);
case DescValueType.BOOLEANTYPE: return d.getBoolean(p);
case DescValueType.STRINGTYPE: return d.getString(p);
case DescValueType.INTEGERTYPE: return d.getInteger(p);
case DescValueType.LARGEINTEGERTYPE: return d.getLargeInteger(p);
case DescValueType.DOUBLETYPE: return d.getDouble(p);
case DescValueType.ALIASTYPE: return d.getPath(p);
case DescValueType.CLASSTYPE: return d.getClass(p);
case DescValueType.UNITDOUBLE: return (d.getUnitDoubleValue(p));
case DescValueType.ENUMERATEDTYPE: return { type: t2s(d.getEnumerationType(p)), value: t2s(d.getEnumerationValue(p)) };
default: break;
};
}
this.getScriptSettings = function () {
var d = null;
try { d = getCustomOptions(UUID) } catch (e) { }
return d ? this.descToObject(d, new Config()) : new Config();
}
this.putScriptSettings = function (o) {
var d = objToDesc(o)
putCustomOptions(UUID, d)
function objToDesc(o) {
var d = new ActionDescriptor;
for (var i = 0; i < o.reflect.properties.length; i++) {
var k = o.reflect.properties[i].toString();
if (k == '__proto__' || k == '__count__' || k == '__class__' || k == 'reflect') continue;
var v = o[k];
k = s2t(k);
switch (typeof (v)) {
case 'boolean': d.putBoolean(k, v); break;
case 'number': d.putInteger(k, v); break;
case 'string': d.putString(k, v); break;
}
}
return d;
}
}
}
function Config() {
this.relinkMode = 0
this.matchExtension = false
this.collect = false
this.subfolder = 'assets'
this.groupByExtension = true
this.move = false
this.checkEmbedded = true
this.targetFolder = ''
}
function CommonFunctions() {
this.getSelectedLayersIds = function () {
if (!apl.getProperty('numberOfDocuments')) return []
var targetLayers = doc.hasProperty('targetLayersIDs') ? doc.getProperty('targetLayersIDs') : [],
selection = [];
if (targetLayers) {
for (var i = 0; i < targetLayers.count; i++) {
selection.push(targetLayers.getReference(i).getIdentifier(stringIDToTypeID('layerID')))
}
}
return selection;
}
this.buildSmartObjectsTree = function () {
if (!apl.getProperty('numberOfDocuments')) return null
var smartObjects = getSmartObjectsList(),
len = 0;
if (cfg.checkEmbedded) {
for (var a in smartObjects) if (!(lr.getProperty('smartObject', a).value.getBoolean(stringIDToTypeID('linked')))) len++
}
this.isDitry = Boolean(len)
var progress = progressWindow(str.findEmbedded, '', len)
if (len && cfg.checkEmbedded) progress.show()
enumSmartObjects(smartObjects, len, progress)
progress.close()
return smartObjects.toSource() == '({})' ? null : smartObjects
}
this.buildFilesList = function (smartObjects) {
var filesList = [];
if (smartObjects != null) filesList = findEqualFiles(collectFiles(smartObjects));
function collectFiles(so, filesList) {
filesList = filesList ? filesList : []
for (var a in so) {
if (so[a].fileReference) filesList.push(describeFile(so[a], Number(a))) else collectFiles(so[a], filesList)
}
return filesList
}
function describeFile(o, id) {
o.sameFolder = null
o.missed = null
o.relink = null
o.relinked = null
allowedExtensions[o.link.extension.toUpperCase()] = true
return o
}
filesList.checkFiles = function () {
for (var i = 0; i < this.length; i++) {
with (this[i]) {
var cur = this[i].relink ? relink : link
if (cur instanceof File) {
sameFolder = documentFolder != null ? (decodeURI(cur).toUpperCase().indexOf(decodeURI(documentFolder).toUpperCase()) == 0 ? true : false) : null
missed = !cur.exists
} else {
sameFolder = false
missed = true
}
}
}
return this
}
return filesList
}
this.splitFilename = function (f) {
var s;
if (f) {
if (f instanceof File) { s = decodeURI(f.name) }
else {
s = f.filename ? f.filename : f
f = {}
}
f.filename = s.substr(0, s.lastIndexOf('.'))
f.extension = (s.substr(s.lastIndexOf('.') + 1, s.length))
}
return f
}
this.findLinks = function (parentFolder, files, selected) {
var fileCache = {},
toDo = {},
len = selected ? selected.length : files.length;
for (var i = 0; i < len; i++) {
var index = selected ? (selected[i]).index : i
toDo[index] = files[index]
}
if (len) {
for (var a in toDo) {
if (toDo[a].sameFolder) {
with (toDo[a]) {
relink = null;
var cur = link,//relink ? relink : link,
folders = parentFolder != null && cur instanceof File ? (decodeURI(cur.path).replace(new RegExp(decodeURI(parentFolder), 'i'), '').split('/')) : [];
}
var link = findSimilarFiles(cur.filename, cur.extension, folders, parentFolder);
if (link) {
files[a].relink = decodeURI(link).toUpperCase() == decodeURI(toDo[a].link).toUpperCase() ? null : link;
delete toDo[a]
}
}
}
{
var len = getObjectLength(toDo)
if (len) {
var c = 1;
app.updateProgress(c++, len + 1)
var allFiles = enumAllFiles(parentFolder)
for (var a in toDo) {
var cur = toDo[a]
app.changeProgressText(str.searchFile + cur.fileReference)
app.updateProgress(c++, len + 1)
var hash = stringIDToTypeID(cur.link.filename)
if (allFiles[hash]) {
var cur = allFiles[hash],
link = null;
for (var i = 0; i < cur.length; i++)
if (files[a].link.extension.toUpperCase() == cur[i].extension.toUpperCase()) {
link = cur[i]
break;
}
if (!link && !cfg.matchExtension) link = cur[0]
if (link) {
files[a].relink = decodeURI(link).toUpperCase() == decodeURI(toDo[a].link).toUpperCase() ? null : link
delete toDo[a]
}
}
}
}
}
}
function findSimilarFiles(fle, ext, dir, parent) {
var subPath = '',
len = dir.length;
for (var i = 0; i < len; i++) {
if (dir[i] == '') continue;
subPath = '/' + dir[i] + '/' + subPath
var f = checkFile(fle, ext, parent + subPath)
if (f) return f
}
var f = checkFile(fle, ext, parent)
return f ? f : null
function checkFile(fle, ext, pth) {
var f = new File(pth + '/' + fle + '.' + ext)
if (f.exists) {
return splitFilename(f)
}
else if (!cfg.matchExtension) {
fle = fle.toUpperCase()
var p = new Folder(pth)
if (p.exists) {
if (!fileCache[pth]) fileCache[pth] = p.getFiles()
var cur = fileCache[pth];
for (var i = 0; i < cur.length; i++) {
if (cur[i] instanceof File) {
var f = splitFilename(cur[i]);
if (f.filename.toUpperCase() == fle && allowedExtensions[f.extension.toUpperCase()]) return f
}
}
}
}
return null;
}
}
function enumAllFiles(parent, listOfFiles) {
app.changeProgressText(str.buildFileList + decodeURI(Folder(parent).name))
listOfFiles = listOfFiles ? listOfFiles : {};
var files = Folder(parent).getFiles();
for (var i = 0; i < files.length; i++) {
app.updateProgress(i + 1, files.length)
var cur = files[i];
if (cur instanceof File) {
if (!cur.hidden) {
cur = splitFilename(cur)
if (allowedExtensions[cur.extension.toUpperCase()]) {
var hash = stringIDToTypeID(cur.filename)
if (!listOfFiles[hash]) {
listOfFiles[hash] = [cur]
} else {
listOfFiles[hash].push(cur)
}
}
}
} else if (cur instanceof Folder) {
enumAllFiles(cur, listOfFiles, listOfFiles)
}
}
return listOfFiles
}
}
this.collectAssets = function (parentFolder, files) {
if (!parentFolder) return
var subfolder = '/' + cfg.subfolder.replace(/[~#%&*{}:<>?|\'-]/g, '_') + '/'
if (!subfolder.replace(/[\/ .]/g, '').length) subfolder = '/'
var progress = new progressWindow(str.copyAssets, '', files.length);
progress.show()
for (var i = 0; i < files.length; i++) {
progress.updateProgress(files[i].fileReference)
if (files[i].missed) continue;
var cur = files[i].relink ? files[i].relink : files[i].link,
target = splitFilename(new File(parentFolder + subfolder + (cfg.groupByExtension ? '/' + cur.extension.toUpperCase() + '/' : '') + cur.filename + '.' + cur.extension));
if (target.exists && (decodeURI(target).toUpperCase() == decodeURI(cur).toUpperCase())) {
continue;
} else {
target = createUniqueFileName(target)
}
if (!(Folder(target.path).exists)) Folder(target.path).create()
if (cfg.collect && cfg.move) {
if (documentFolder != null && decodeURI(cur).toUpperCase().indexOf(decodeURI(documentFolder).toUpperCase()) == 0) {
if (!target.exists) {
if (cur.rename(target)) {
files[i].relink = target
} else {
if (cur.copy(target)) {
cur.remove()
files[i].relink = target
}
}
var f = Folder(cur.path)
if (f.getFiles().length == 0) {
f.remove()
do {
f = Folder(f.path)
if (f.getFiles().length) break;
} while (f.remove())
}
}
}
}
if (files[i].relink != target && cur.copy(target)) files[i].relink = target
}
progress.close()
function createUniqueFileName(target) {
var parent = decodeURI(target.path),
f = target,
c = 1;
while (f.exists) {
f = splitFilename(new File(parent + '/' + target.filename + ' (' + c + ').' + target.extension))
c++;
}
return f
}
}
this.relink = function (files, smartObjects) {
var toRelink = [],
linkedObjects = {},
embeddedObjects = {},
locked = unlockLayers();
for (var i = 0; i < files.length; i++) if (files[i].relink) toRelink.push(files[i])
for (var a in smartObjects) {
if (smartObjects[a].link) { linkedObjects[a] = smartObjects[a] }
else { embeddedObjects[a] = flattenObject(smartObjects[a]) }
}
if (toRelink.length) {
var progress = new progressWindow(str.relink, '', toRelink.length)
progress.show()
for (var a in linkedObjects) {
for (var i = 0; i < toRelink.length; i++) {
if (isEqualObject(linkedObjects[a].link, toRelink[i].link)) {
lr.selectLayerByIDList([Number(a)])
progress.updateProgress(lr.getProperty('name', Number(a)))
if (lr.relinkCurrentLayer(toRelink[i].relink)) toRelink[i].relinked = true;
break;
}
}
}
for (var a in embeddedObjects) {
var toDelete = [],
found = false;
for (var i = 0; i < toRelink.length; i++) {
for (var x = 0; x < embeddedObjects[a].length; x++) {
if (isEqualObject(embeddedObjects[a][x].link, toRelink[i].link)) {
found = true;
break;
}
if (found) break;
}
}
if (found) {
for (var i = 0; i < embeddedObjects[a].length; i++) {
if (embeddedObjects[a][i].link instanceof File && embeddedObjects[a][i].link.exists) continue;
var f = createDumbFile(documentFolder, embeddedObjects[a][i].fileReference)
if (f) toDelete.push(f)
}
lr.selectLayerByIDList([Number(a)])
progress.updateProgress(lr.getProperty('name', Number(a)))
if (lr.editSmartObject()) {
relinkEmbedded(toRelink)
doc.closeDocument(true)
}
for (var i = 0; i < toDelete.length; i++) {
toDelete[i].remove()
}
}
}
lockLayers(locked)
}
function relinkEmbedded(files) {
var len = doc.getProperty('numberOfLayers'),
locked = unlockLayers();
if (len) {
for (var i = len; i >= 1; i--) {
if (lr.hasProperty('smartObject', i, true)) {
var id = lr.getProperty('layerID', i, true),
smartObject = lr.descToObject(lr.getProperty('smartObject', i, true).value);
if (smartObject.linked) {
for (var x = 0; x < files.length; x++) {
if (isEqualObject(splitFilename(smartObject.link), files[x].link)) {
doc.selectLayerByIDList([id])
if (lr.relinkCurrentLayer(files[x].relink)) files[x].relinked = true
break;
}
}
} else {
doc.selectLayerByIDList([id])
if (doc.convertSmartObjectToLayers()) {
var cur = flattenObject(enumSmartObjects(getSmartObjectsList(lr.getProperty('itemIndex'), true)))
doc.deleteCurrentHistoryState()
var found = false;
do {
var f = splitFilename((cur.shift()).link)
for (var x = 0; x < files.length; x++) {
if (isEqualObject(f, files[x].link)) {
found = true
break;
}
}
if (found) break;
} while (cur.length)
if (true) {
doc.selectLayerByIDList([id])
if (lr.editSmartObject()) {
relinkEmbedded(files)
doc.closeDocument(true)
}
}
}
}
}
}
}
lockLayers(locked)
}
function flattenObject(o, output) {
output = output ? output : [];
for (var a in o) {
if (o[a].link) {
output.push(o[a])
} else {
flattenObject(o[a], output)
}
}
return output
}
function isEqualObject(a, b) {
for (var i in a) {
if (stringIDToTypeID(a[i]) != stringIDToTypeID(b[i])) return false
}
return true
}
}
this.createReport = function (smartObjects, filesList, err) {
var report = [],
s2t = stringIDToTypeID;
report.push(new Date + '\nSend this report to jazz-y@ya.ru')
if (err) report.push('Error:\n' + err.toSource())
if (smartObjects) report.push('Smart objects:\n' + smartObjects.toSource())
if (filesList) report.push('Files:\n' + filesList.toSource())
report.push('Application:')
try {
(d = new ActionDescriptor()).putObject(s2t('object'), s2t('object'), apl.getProperty());
report.push(executeAction(s2t('convertJSONdescriptor'), d).getString(s2t('json')))
} catch (e) { report.push(e.toSource()) }
report.push('Documents:')
if (len = apl.getProperty('numberOfDocuments')) {
try {
for (var i = 1; i <= len; i++) {
(d = new ActionDescriptor()).putObject(s2t('object'), s2t('object'), doc.getProperty(null, i, true));
report.push(executeAction(s2t('convertJSONdescriptor'), d).getString(s2t('json')))
}
} catch (e) { report.push(e.toSource()) }
}
report.push('Layers of active document:')
if (len = doc.getProperty('numberOfLayers')) {
try {
for (var i = 1; i <= len; i++) {
(d = new ActionDescriptor()).putObject(s2t('object'), s2t('object'), lr.getProperty(null, i, true));
report.push(executeAction(s2t('convertJSONdescriptor'), d).getString(s2t('json')))
}
} catch (e) { report.push(e.toSource()) }
}
var f = new File('~/desktop/' + str.scriptName + ' errorLog.txt');
if (f.saveDlg(str.saveLog)) {
f.open('w')
f.write(report.join('\n\n'))
f.close()
}
}
function unlockLayers() {
var len = doc.getProperty('numberOfLayers'),
layers = {};
if (len) {
for (var i = len; i >= 1; i--) {
if (lr.getProperty('layerSection', i, true).value == 'layerSectionEnd') continue;
var locking = lr.getProperty('layerLocking', i, true).value
if (checkLocking(locking)) {
layers[lr.getProperty('layerID', i, true)] = lr.getProperty('layerLocking', i, true).value;
lr.applyLocking(undefined, i, true)
}
}
}
return layers;
}
function lockLayers(o) {
for (var a in o) {
lr.applyLocking(o[a], a)
}
}
function checkLocking(d) {
var o = lr.descToObject(d);
for (var a in o) if (o[a]) return true
return false
}
function enumSmartObjects(ids, progressLength, w) {
for (var a in ids) {
var cur = doc.descToObject(lr.getProperty('smartObject', a).value)
if (cur.linked && !checkLocking(lr.getProperty('layerLocking', a).value)) {
if (cur.link && cur.link.type && cur.link.type == 'ccLibrariesElement') {
delete ids[a];
continue;
}
ids[a] = describeSmartObject(cur)
} else {
if (cfg.checkEmbedded) {
if (progressLength) w.updateProgress(lr.getProperty('name', a))
doc.selectLayerByIDList([a])
if (doc.convertSmartObjectToLayers()) {
ids[a] = enumSmartObjects(getSmartObjectsList(lr.getProperty('itemIndex'), true))
doc.deleteCurrentHistoryState()
}
}
if (ids[a] == null || ids[a].toSource() == '({})') delete ids[a]
}
}
return ids
function describeSmartObject(o) {
return {
link: splitFilename(!o.link || o.link == '' ? o.fileReference : File(o.link)),
fileReference: o.fileReference,
}
}
}
function getSmartObjectsList(idx, mode) {
var indexFrom = idx ? (doc.getProperty('hasBackgroundLayer') ? --idx : idx) : doc.getProperty('numberOfLayers'),
indexTo = doc.getProperty('hasBackgroundLayer') ? 0 : 1;
return buildSmartObjectsList(enumLayers(indexFrom, indexTo, mode))
function enumLayers(from, to, currentSection, parentItem, group) {
parentItem = parentItem ? parentItem : [];
var isDirty = false;
for (var i = from; i >= to; i--) {
var layerSection = lr.getProperty('layerSection', i, true).value;
if (layerSection == 'layerSectionStart') {
i = enumLayers(i - 1, to, undefined, [], parentItem)
isDirty = true;
if (currentSection) return parentItem[0]
continue;
}
if (currentSection && isDirty) return []
var properties = {};
properties.layerKind = lr.getProperty('layerKind', i, true)
properties.id = lr.getProperty('layerID', i, true)
properties.smartObject = lr.hasProperty('smartObject', i, true)
if (layerSection == 'layerSectionEnd') {
for (o in properties) { parentItem[o] = properties[o] }
group.push(parentItem);
return i;
} else {
parentItem.push(properties)
if (currentSection && !isDirty) return parentItem
}
}
return parentItem
}
function buildSmartObjectsList(layers, list) {
if (!list) list = {}
for (var i = 0; i < layers.length; i++) {
var cur = layers[i]
if (cur.length) {
buildSmartObjectsList(cur, list)
} else {
if (cur.smartObject && cur.layerKind == 5) list[cur.id] = null
}
}
return list
}
}
function getObjectLength(o) {
var i = 0
for (var a in o) i++
return i
}
function findEqualFiles(files, relinkMode) {
var o = [], f = files.slice();
do {
var cur = f.shift()
if (cur) {
for (var i = 0; i < f.length; i++) {
if (!relinkMode) {
if (f[i] instanceof File) {
if (decodeURI(cur.link).toUpperCase() == decodeURI(f[i].link).toUpperCase()) f[i] = null
} else {
if (f[i] && (cur.link.filename + cur.link.extension).toUpperCase() == (f[i].link.filename + f[i].link.extension).toUpperCase()) f[i] = null
}
}
else {
if (f[i] && decodeURI(cur.relink).toUpperCase() == decodeURI(f[i].relink).toUpperCase()) f[i] = null
}
}
o.push(cur)
}
} while (f.length)
return o
}
function createDumbFile(parent, reference) {
var f = new File(parent + '/' + reference);
if (f.exists) return null
f.open('w')
f.close()
return f
}
function progressWindow(title, message, max) {
var w = new Window('palette', title),
bar = w.add('progressbar', undefined, 0, max),
stProgress = w.add('statictext', undefined, message);
stProgress.preferredSize = [350, 20]
stProgress.alignment = 'left'
bar.preferredSize = [350, 20]
w.updateProgress = function (message) {
bar.value++;
if (message) stProgress.text = bar.value + '/' + max + ': ' + message
w.update();
}
return w;
}
splitFilename = this.splitFilename;
documentFolder = this.documentFolder = doc.hasProperty('fileReference') ? Folder(File(doc.getProperty('fileReference').fsName).path) : null;
isDitry = this.isDitry = false;
targetFolder = this.targetFolder = null;
}
function Locale() {
this.ico = {
red: "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\n\x00\x00\x00\n\b\x06\x00\x00\x00\u008D2\u00CF\u00BD\x00\x00\x00;IDAT\x18\u0095c\u00FCci\u00F3\u009F\u0081\b\u00C0\x02R\u00C2((\u0084W\u00E5\u00FF\u00F7\u00EF \n\u00C1\u0080\u0087\x17\u00BB\u00AA/\u009F\u00C1\x14\x131\u00D6\x0E\x15\u0085\b_C}\u0087W!(\u009C\u00F0\x02\x06\x06\x06\x00\x18\u00EF\fO\u0083\b\u00CC\u00FD\x00\x00\x00\x00IEND\u00AEB`\u0082",
green: "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\n\x00\x00\x00\n\b\x06\x00\x00\x00\u008D2\u00CF\u00BD\x00\x00\x00=IDAT\x18\u0095c\u00F4\u00DEo\u00F8\u009F\u0081\b\u00C0\x02R\"\u00C6\u00C9\u008FW\u00E5\u00AB\u00EF\x1F!\nA@\u0090\u009D\x1B\u00AB\u00A2\u00F7?\u00BF\u0082i&b\u00AC\x1D*\n\u00E1\u00BE\u0086\u00F9\x0E\u00AFBP8\u00E1\x05\f\f\f\x000\x1F\x0E\x05z4V\u0094\x00\x00\x00\x00IEND\u00AEB`\u0082",
yellow: "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\n\x00\x00\x00\n\b\x06\x00\x00\x00\u008D2\u00CF\u00BD\x00\x00\x00;IDAT\x18\u0095c\u00FCp@\u00ED?\x03\x11\u0080\x05\u00A4\u0084\u0089\u0093\x1B\u00AF\u00CA\x7F\u00DF\u00BFB\x14\u0082\x15\u00B3s`W\u00F4\u00F3\x07D\u009E\x18k\u0087\u008AB\u00B8\u00AFa\u00BE\u00C3\u00AB\x10\x14Nx\x01\x03\x03\x03\x00+\u00AA\x0E\u00CA*\u0090\u00C3\u00A2\x00\x00\x00\x00IEND\u00AEB`\u0082",
all: "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\n\x00\x00\x00\n\b\x06\x00\x00\x00\u008D2\u00CF\u00BD\x00\x00\x00\u00B5IDAT\x18\u0095\u008D\u0090K\n\u00840\x10D+C\u00FC\u00E5\x06\u00D9\u00B8\u00F7\n\"nt\u00E3\u0089\u00C5\u0095\u00E0!\u00B2\u00CC-\x14\u00F1\u0097\u00A1{\u00B0a\x10\u0086)(:\u00A4_WB\u00AB\u00BE\u00EF\x03\u00FE\u0090&\u00A4\u00AEk!\u0097e\u0081R\nY\u0096\u00C9\u00DD4M\x1F\u0090\u0094\u00A6)\u009Cs\u00F0\u00DE#I\x12\u00E4y\u008E\u00A2(x\x10w\"i]W\u0086\u00BA\u00AE\u00C3\u00BE\u00EF\x18\u00C7\x11\u00D6Z\u00C4q\u00CC\u00FD\u00D7\r\x1E\u00C7\u00C15\u008A\"N$x\u009Egy^\x12\u00E9_\u00D7ua\x18\x06i\u009E\u00E7)gI\f!0\u00DC\u00B6-\u009A\u00A6\u0091\u00E1G\u00A2\u00D6\u009A\u00BDm\x1B\x03dc\u00CC\x13\u00A4\u00C4\u00B2,\u00B9\u0092\u00AB\u00AA\u00E2M|\u0081\u00B4\u00A7\u009F\x02\u00F0\x06\x05\"C\u00AEZ\f\u00AC\x18\x00\x00\x00\x00IEND\u00AEB`\u0082"
}
this.scriptName = 'Relink layers'
this.errNoFiles = { ru: 'Š”Š²ŃŠ·Š°Š½Š°Š½Š½Ńе ŃŠ»Š¾Šø не найГенŃ!', en: 'Linked layers not found!' }
this.findEmbedded = { ru: 'ŠŠ¾ŠøŃŠŗ ŃŠ²ŃзаннŃŃ
ŃŠ°Š¹Š»Š¾Š² в ŃŠ¼Š°ŃŃ-Š¾Š±ŃŠµŠŗŃаŃ
', en: 'Find linked files in smart objects' }
this.searchFile = { ru: 'ŠŠ¾ŠøŃŠŗ ŃŠ°Š¹Š»Š°: ', en: 'Search file: ' }
this.buildFileList = { ru: 'ŠŠ±ŃŠ°Š±Š¾ŃŠŗŠ° ŠŗŠ°ŃŠ°Š»Š¾Š³Š°: ', en: 'Build list of files: ' }
this.copyAssets = { ru: 'Š”Š±Š¾ŃŠŗŠ° ŃŠµŃŃŃŃŠ¾Š²', en: 'Collect assets' }
this.relink = { ru: 'Š”Š²ŃŠ·Š°ŃŃ Š·Š°Š½Š¾Š²Š¾', en: 'Relink layers' }
this.linkedFiles = { ru: 'Š”Š²ŃŠ·Š°Š½Š½Ńе ŃŠ°Š¹Š»Ń', en: 'Linked files' }
this.findInEmbedded = { ru: 'ŠæŠ¾ŠøŃŠŗ ŃŠ²ŃзаннŃŃ
ŃŠ°Š¹Š»Š¾Š² внŃŃŃŠø ŃŠ¾Š“ŠµŃŠ¶ŠøŠ¼Š¾Š³Š¾ вŃŃŃŠ¾ŠµŠ½Š½ŃŃ
ŃŠ¼Š°ŃŃ-Š¾Š±ŃŠµŠŗŃов', en: 'find links inside contents of embedded smart objects' }
this.relinkAll = { ru: 'Š”Š²ŃŠ·Š°ŃŃ Š·Š°Š½Š¾Š²Š¾', en: 'Relink all' }
this.extension = { ru: 'ŃŃŠøŃŃŠ²Š°ŃŃ ŃŠ°ŃŃŠøŃение', en: 'match file extension' }
this.subfolder = { ru: 'ŃŠ¾Š±ŃаŃŃ ŃŠµŃŃŃŃŃ Š² папке:', en: 'collect assets in subfolder:' }
this.groupByExt = { ru: 'гŃŃŠæŠæŠøŃŠ¾Š²Š°ŃŃ ŠæŠ¾ ŃŠøŠæŃ', en: 'group by extension' }
this.move = { ru: 'ŠæŠµŃŠµŠ¼ŠµŃаŃŃ', en: 'move files' }
this.cancel = { ru: 'ŠŃмена', en: 'Cancel' }
this.currentPath = { ru: 'папка Š¾ŃŠŗŃŃŃŠ¾Š³Š¾ Š“Š¾ŠŗŃŠ¼ŠµŠ½Ńа', en: 'current document path' }
this.newPath = { ru: 'Š²ŃŠ±ŃаŃŃ ŠæŠ°ŠæŠŗŃ...', en: 'choose folder...' }
this.relinkSelected = { ru: 'Š”Š²ŃŠ·Š°ŃŃ Š²ŃŠ±ŃŠ°Š½Š½ŃŠµ', en: 'Relink selected' }
this.replaceFor = { ru: 'ŠŠ°Š¼ŠµŠ½ŠøŃŃ ŃŠ²ŃŠ·Ń Š“Š»Ń ', en: 'Replace link for ' }
this.strRelinkErr1 = { ru: 'ŠŠ°Š¼ŠµŠ½Š° ŃŠ²ŃŠ·Šø не Š¼Š¾Š¶ŠµŃ бŃŃŃ Š²ŃŠæŠ¾Š»Š½ŠµŠ½Š°!', en: 'Relink operation cannot be performed!' }
this.strRelinkErr2 = { ru: ' ŃŠøŠæ ŃŠ°Š¹Š»Š¾Š² не ŠæŠ¾Š“Š“ŠµŃŠ¶ŠøŠ²Š°ŠµŃŃŃ!', en: ' files does not supported!' }
this.strRelinkErr3 = { ru: 'ŠŃŃŃ Š½ŠµŠ“Š¾ŃŃŃŠæŠµŠ½!\n\n- Š°ŠŗŃŠøŠ²Š½Ńй Š“Š¾ŠŗŃŠ¼ŠµŠ½Ń не ŃŠ¾Ń
ŃŠ°Š½ŠµŠ½!', en: 'Path not found!\n\n- active document not saved yet!' }
this.err = { ru: 'ŠŃибка', en: 'Error' }
this.warning = { ru: 'ŠŃŠµŠ“ŃŠæŃежГение', en: 'Warning' }
this.errGlobal = { ru: 'ŠŃŠ¾ŠøŠ·Š¾ŃŠ»Š° Š¾ŃŠøŠ±ŠŗŠ° во Š²ŃŠµŠ¼Ń Š²ŃŠæŠ¾Š»Š½ŠµŠ½ŠøŃ ŃŠŗŃŠøŠæŃŠ°. ДозГаŃŃ ŃŠ°Š¹Š» оŃŃŠµŃа?', en: 'An error occurred while executing the script. Create a report file? ' }
this.saveLog = { ru: 'Файл оŃŃŠµŃа', en: 'Report file' }
this.select = { ru: 'ŠŃŃŃŃŃŠ¹ Š²ŃŠ±Š¾Ń:', en: 'Quick select:' }
this.target = { ru: 'ŠŃŃŃ Š½Š°Š·Š½Š°ŃŠµŠ½ŠøŃ:', en: 'Target path:' }
}
function mainWindow(fileList, runMode) {
var w = new Window("dialog {text: '" + str.linkedFiles + " " + ver + "'}"),
gSelect = w.add("group{alignChildren: ['left','center'], alignment: ['fill','top']}"),
stSelect = gSelect.add("statictext{text:'" + str.select + "'}"),
iAll = gSelect.add("iconbutton", undefined, str.ico.all, { style: "toolbutton" }),
iGreen = gSelect.add("iconbutton", undefined, str.ico.green, { style: "toolbutton" }),
iYellow = gSelect.add("iconbutton", undefined, str.ico.yellow, { style: "toolbutton" }),
iRed = gSelect.add("iconbutton", undefined, str.ico.red, { style: "toolbutton" }),
l = w.add("listbox", [0, 0, 600, 400], undefined, { multiselect: true }),
gEmbedded = w.add("group{alignChildren: ['left','center'], alignment: ['fill','top']}"),
chEmbedded = gEmbedded.add("checkbox{text: '" + str.findInEmbedded + "', preferredSize: [70, -1]}"),
pnRelink = w.add("panel{text: '" + str.linkedFiles + "', alignChildren: ['left','center'], alignment: ['fill','top'], margins:[10,20,10,10]}"),
gRelink = pnRelink.add("group{alignChildren: ['left','center'], alignment: ['fill','top']}"),
rl = gRelink.add("button{text: '" + str.relinkAll + "', preferredSize: [170, -1]}"),
dl = gRelink.add("dropdownlist", undefined, undefined, { items: [str.currentPath, str.newPath] }),
chMatch = gRelink.add("checkbox{text: '" + str.extension + "', preferredSize: [120, -1]}"),
gPath = pnRelink.add("group{alignChildren: ['left','center'], alignment: ['fill','top']}"),
stPathLabel = gPath.add("statictext{text:'" + str.target + "'}"),
stPath = gPath.add("statictext{preferredSize: [450, -1]}"),
gCollect = w.add("group{alignChildren: ['left','center'], alignment: ['fill','top']}"),
chCollect = gCollect.add("checkbox{text: '" + str.subfolder + "', preferredSize: [170, -1]}"),
gSubCollect = gCollect.add("group{alignChildren: ['left','center'], alignment: ['fill','top']}"),
et = gSubCollect.add("edittext{preferredSize: [100, -1]}"),
chGroup = gSubCollect.add("checkbox{text: '" + str.groupByExt + "', preferredSize: [120, -1]}"),
chMove = gSubCollect.add("checkbox{text: '" + str.move + "', preferredSize: [70, -1]}"),
gButtons = w.add("group"),
bnOk = gButtons.add("button {text:'Ok'}", undefined, undefined, { name: "ok" }),
bnCancel = gButtons.add("button {text:'" + str.cancel + "'}", undefined, undefined, { name: "cancel" });
l.graphics.font = "dialog:12";
l.fillLinksList = function (items) {
var currentSelection = []
if (this.selection) for (var i = 0; i < this.selection.length; i++) currentSelection.push((this.selection[i]).index)
this.removeAll()
for (var i = 0; i < items.length; i++) {
with (items[i]) {
var cur = relink ? relink : link
this.add('item', cur instanceof File ?
((relink ? ' ā ' : (missed ? ' ' : ' ā ')) + (relink ? (relink).fsName : link.fsName)) :
(' ' + items[i].fileReference));
this.items[i].image = items[i].missed ? str.ico.red : (items[i].sameFolder ? str.ico.green : str.ico.yellow)
}
}
if (currentSelection.length) this.selection = currentSelection
}
l.onClick = function () {
rl.text = l.selection != null ? str.relinkSelected + (l.selection.length > 1 ? ' (' + l.selection.length + ')' : '') : str.relinkAll
}
l.onDoubleClick = function () {
if (l.selection != null) {
if ((l.selection[0]).index >= 0) {
with (fileList[(l.selection[0]).index]) {
var cur = link instanceof File ? link : new File,
f = fn.splitFilename(cur.openDlg(str.replaceFor + (link instanceof File ? decodeURI(link.name) : fileReference), '', false));
if (f && f.exists) {
if (allowedExtensions[f.extension.toUpperCase()]) {
relink = decodeURI(link).toUpperCase() == decodeURI(f).toUpperCase() ? null : f;
l.fillLinksList(fileList.checkFiles())
} else { alert(str.strRelinkErr1 + '\n\n*.' + f.extension + str.strRelinkErr2, str.err, 1) }
}
}
}
}
}
rl.onClick = function () {
if (fn.targetFolder) {
app.doForcedProgress('', 'fn.findLinks(fn.targetFolder, fileList, l.selection)')
l.fillLinksList(fileList.checkFiles())
}
}
dl.onChange = function () {
switch (this.selection.index) {
case 0:
fn.targetFolder = fn.documentFolder ? fn.documentFolder : null;
if (!fn.targetFolder && w.visible) alert(str.strRelinkErr3, str.err, 1)
break;
case 1:
if (w.visible) {
var p = (new Folder(fn.documentFolder ? fn.documentFolder : null)).selectDlg()
fn.targetFolder = p ? p : null;
} else {
fn.targetFolder = cfg.targetFolder != '' && Folder(cfg.targetFolder).exists ? Folder(cfg.targetFolder) : null
}
break;
}
cfg.relinkMode = this.selection.index
cfg.targetFolder = fn.targetFolder ? fn.targetFolder.fsName : ''
rl.enabled = bnOk.enabled = fn.targetFolder && fileList.length ? true : false;
stPath.text = fn.targetFolder ? fn.targetFolder.fsName : ''
}
chCollect.onClick = function () { gSubCollect.enabled = cfg.collect = this.value }
chGroup.onClick = function () { cfg.groupByExtension = this.value }
chMove.onClick = function () { cfg.move = this.value }
bnOk.onClick = function () {
if (fn.targetFolder == null) { rl.onClick(true) }
w.close(1);
(new AM()).putScriptSettings(cfg);
if (fn.targetFolder && cfg.collect) fn.collectAssets(fn.targetFolder, fileList)
}
chEmbedded.onClick = function () {
cfg.checkEmbedded = this.value
w.close(3)
}
et.onChange = function () {
cfg.subfolder = this.text
}
bnCancel.onClick = function () { fileList = []; w.close(2) }
chMatch.onClick = function () { cfg.matchExtension = this.value }
iAll.onClick = function () { qickSelect(0) }
iGreen.onClick = function () { qickSelect(1) }
iYellow.onClick = function () { qickSelect(2) }
iRed.onClick = function () { qickSelect(3) }
w.onShow = function () {
if (!fn.documentFolder) cfg.relinkMode = 1
dl.selection = cfg.relinkMode ? 1 : 0
chMatch.value = cfg.matchExtension
gSubCollect.enabled = chCollect.value = cfg.collect
et.text = cfg.subfolder
chGroup.value = cfg.groupByExtension
chMove.value = cfg.move
chEmbedded.value = cfg.checkEmbedded
chEmbedded.enabled = runMode
stPath.text = fn.targetFolder ? fn.targetFolder.fsName : ''
dl.size.width = w.size.width - rl.size.width - chMatch.size.width - 90
et.size.width = w.size.width - chGroup.size.width - chCollect.size.width - chMove.size.width - 80
w.layout.layout(true)
l.fillLinksList(fileList.checkFiles())
}
function qickSelect(mode) {
var selection = [],
len = fileList.length;
l.selection = selection;
switch (mode) {
case 1: for (var i = 0; i < len; i++) if (fileList[i].sameFolder && !fileList[i].missed) selection.push(i); break;
case 2: for (var i = 0; i < len; i++) if (!fileList[i].sameFolder && !fileList[i].missed) selection.push(i); break;
case 3: for (var i = 0; i < len; i++) if (fileList[i].missed) selection.push(i); break;
default: for (var i = 0; i < len; i++) selection.push(i);
}
l.selection = selection
l.onClick()
}
return w
}
Copy link to clipboard
Copied
to jazz-y
ŠŃивеŃ. ŠŃŠøŠŗŠ¾Š»ŃŠ½Ńй ŃŠŗŃŠøŠæŃ. ŠŠ°Š¶Šµ ŃŃŃŠ°Ńно ŃŠ°Š·Š±ŠøŃаŃŃŃŃ ŠŗŠ°Šŗ он ŃŠ°Š±Š¾ŃаеŃ.
ŠŠ¾Š“ŃŠŗŠ°Š¶Ń иГеŃ, Š¼Š¾Š¶ŠµŃ ŠŗŠ°Šŗ-ŃŠ¾ ŠæŠ¾Š¼Š¾Š¶ŠµŃ ŠøŠ»Šø ŃŃŠŗŠ¾ŃŠøŃ. Š”ŃŠ“Ń ŠæŠ¾ ŠŗŠ¾Š“Ń ŃŃ ŠµŃ Š½Šµ ŠøŃŠæŠ¾Š»ŃŠ·ŃеŃŃ (а Š¼Š¾Š¶ŠµŃ ŃŃŠ¾ Ń ŃŠ»ŠµŠæŠ¾Š¹). ŠŠµ Š·Š½Š°Ń (в коГ не ŃŠ¼Š¾ŃŃŠµŠ») как ŃŃ ŠæŠ¾Š»ŃŃŠ°ŠµŃŃ Š»ŠøŠ½ŠŗŠø, в Ń.Ń. Šø ŠøŠ· вложеннŃŃ ŃŠ¼Š°ŃŃ-Š¾Š±ŃŠµŠŗŃов, но ŠµŃли в Š“ŠµŃŠŗŃŠøŠæŃŠ¾Ń Š“Š»Ń ŠæŠ¾Š»ŃŃŠµŠ½ŠøŃ "json" Š“Š¾ŠŗŃŠ¼ŠµŠ½Ńа вŃŃŠ°Š²ŠøŃŃ ŠæŠ°ŃŠ°Š¼ŠµŃŃ d.putBoolean(stringIDToTypeID("expandSmartObjects"), true), ŃŠ¾ вŃŃ ŠøŠ½ŃŠ¾ŃŠ¼Š°ŃŠøŃ ŃŃŠ°Š·Ń по Š²Ńем Š¾Š±ŃекŃам, Šø Š²Š»Š¾Š¶ŠµŠ½Š½ŃŠ¼ ŃŠ¾Š¶Šµ, Š±ŃŠ“ŠµŃ Š² ŃŃŠ¾Š¼ Š“Š¶ŃŠ¾Š½Šµ. ŠŃо ŠæŃоиŃŃ Š¾Š“ŠøŃ Š±ŠµŠ· ŃŠ²Š½Š¾Š³Š¾ Š¾ŃŠŗŃŃŃŠøŃ Š²ŃŠµŃ вложеннŃŃ ŃŠ¼Š°ŃŃ-Š¾Š±ŠµŠŗŃŠ¾Š².
ŠŠ«. наŃŃŠµŃ паŃŃŠøŠ½Š³Š° psd ŃŠ¾Š¶Šµ ŠøŠ“ŠµŃ Ń Š¾ŃŠ¾ŃаŃ, но мне, ŃŠµŃŃŠ½Š¾, Š½ŠµŠ¾Ń Š¾ŃŠ° ŃŃŠøŠ¼ занимаŃŃŃŃ. )
ŠŠ«2. мне Š½ŠµŠøŠ½ŃŠµŃŠµŃно, ŠæŠ¾ŃŠ¾Š¼Ń ŃŃŠ¾ Ń Š¼ŠµŠ½Ń CS6. Там линков Š½ŠµŃ по-Š¾ŠæŃŠµŠ“елениŃ. ЄоŃŃ Ń ŃŠ“елал аналог еŃŃ ŠŗŠ¾Š³Š“Š° ДД Šø не ŠæŠ°Ń Š»Š¾. ŠŠ“еŃ: в Š¼ŠµŃаГаннŃŃ ŃŠ»Š¾Ń Ń ŃŠ°Š½ŠøŃŃŃ Š»ŠøŠ½Šŗ на ŃŠ°Š¹Š». ŠŠ°Š¼ŠµŠ½ŠøŃŃ ŃŠ¾Š“ŠµŃŠ¶ŠøŠ¼Š¾Šµ ŃŠ°ŠŗŠ¾Š³Š¾ ŃŠ»Š¾Ń Гаже Ń ŃŃŠµŃом ŃŃŠ°Š½ŃŃŠ¾ŃŠ¼Š°ŃŠøŠø, ŃŠøŠæŠ° маŃŃŃŠ°Š± Šø ŠæŠ¾Š²Š¾ŃŠ¾Ń, на ŃŠ¾Š“ŠµŃŠ¶ŠøŠ¼Š¾Šµ ŃŠ°Š¹Š»Š° Гело ŃŠµŃ ники. Там же (в Š¼ŠµŃаГаннŃŃ ) Ń ŃŠ°Š½ŠøŃŃŃ ŃŠ°Š¹Š¼ŃŃŠ°ŠæŠ¼ Š“Š»Ń ŃŠ°Š¹Š»Š°, ŃŃŠ¾Š± конŃŃŠ¾Š»ŠøŃоваŃŃ Š°ŠŗŃŃŠ°Š»ŃноŃŃŃ ŃŠ¾Š³Š¾, ŃŃŠ¾ ŠæŠ¾ŠŗŠ°Š·ŃŠ²Š°ŠµŃŃŃ Š² ŃŠ»Š¾Šµ.
Copy link to clipboard
Copied
@r-bin оно ŃŠ°Š±Š¾ŃŠ°ŠµŃ ŃŠµŃез convert to layers, ŠŗŠ¾ŃŠ¾ŃŃŠ¹ ŠæŠ¾ŃŠ²ŠøŠ»ŃŃ Š² CC 2020 (ŃŠ¾ еŃŃŃ ŃŠµŠŗŃŃŃŠøŠµŠ¹ ŃŠ°ŃŠŗŃŃŠ²Š°Ń Š²ŃŠµ Š¾Š±ŃŠµŠŗŃŃ Šø ŃŠ¾Š±ŠøŃŠ°Ń ŠæŃŃŠø). Š” JSON еŃŃŃ ŠæŃŠ¾Š±Š»ŠµŠ¼Š° - ŠæŃŠµŠŗŃŠ°ŃŠ½Š¾ ŠæŠ¾ŠŗŠ°Š·ŃŠ²Š°ŠµŃ ŠæŃŃŃ Šø ŃŠ°ŃŃŠøŃение ŠøŃŃ Š¾Š“Š½Š¾Š³Š¾ ŃŠ°Š¹Š»Š° когГа он на меŃŃŠµ, но ŠµŃли ŃŠµŃŃ ŠøŠ“ŠµŃ Š¾ Š±ŠøŃŠ¾Š¹ ŃŃŃŠ»ŠŗŠµ ŃŠ¾ ŠæŠ¾Š“Š¼ŠµŠ½ŃŠµŃ Š¾ŃŠ½Š¾ŃŠøŃŠµŠ»ŃŠ½ŃŠ¹ ŠæŃŃŃ Š½Š° ŠæŠ°ŠæŠŗŃ temp, а ŃŠ°ŃŃŠøŃение на psb. ŠŠ»Ń ŠæŃŠ¾ŃŃŠ¾Š³Š¾ воŃŃŃŠ°Š½Š¾Š²Š»ŠµŠ½ŠøŃ ŃŃŃŠ»Š¾Šŗ (по имени ŃŠ°Š¹Š»Š°) ŃŃŠ¾ ŃŠ¾Š¹Š“еŃ, но Ń ŃŠ»ŠµŠ³ŠŗŠ° ŠæŠ¾Š·Š“Š°Š¼Š¾ŃŠ¾ŃŠøŠ»ŃŃ Šø ŠæŠøŃŠ°Š», ŃŃŠ¾Š±Ń ŃŃŠøŃŃŠ²Š°Š»ŃŃ Š¾ŃŠ½Š¾ŃŠøŃŠµŠ»ŃŠ½ŃŠ¹ ŠæŃŃŃ ŃŠ°Š¹Š»Š° Šø (Š¾ŠæŃŠøŠ¾Š½Š°Š»Ńно) его ŃŠ°ŃŃŠøŃение.
Copy link to clipboard
Copied
THANK YOU, It works better now. (Whenever you got time, it would be awesome if you could reduce waiting time after clicking on relink)
Copy link to clipboard
Copied
@jazz-y wrote:
The main problem is that when opening each file with broken links, Photoshop shows a modal window that stops the script. This completely destroys the ability to batch process multiple files without user interaction.
Hmmm... Changing the idforceNotify boolean to false suppresses the dialog and opens the image with the broken link waiting to be updated.
The following proof of concept script works with a single file with a single layer. I'll look into extending this base operation for a batch:
// Select the file
var theFile = File.openDialog("Please select the file to update the missing link:");
// Select the folder to relink to
var theNewFolder = Folder.selectDialog('Please select the new folder to relink to:');
// Open the file suppressing the missing link dialog
var idopen = stringIDToTypeID("open");
var desc567 = new ActionDescriptor();
var iddontRecord = stringIDToTypeID( "dontRecord" );
desc567.putBoolean( iddontRecord, false );
var idforceNotify = stringIDToTypeID( "forceNotify" );
desc567.putBoolean( idforceNotify, false );
var idnull = stringIDToTypeID( "null" );
desc567.putPath( idnull, new File( theFile ) ); // the active doc
var iddocumentID = stringIDToTypeID( "documentID" );
desc567.putInteger( iddocumentID, 1081 );
executeAction(idopen, desc567, DialogModes.NO);
// Get the missing link path
/*
Based on:
https://stackoverflow.com/questions/63010107/get-a-smart-objects-layers-files-directory-source-in-jsx
*/
ref = new ActionReference();
ref.putProperty(charIDToTypeID("Prpr"), stringIDToTypeID("smartObject"));
ref.putEnumerated(charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt"));
var so = executeActionGet(ref).getObjectValue(stringIDToTypeID("smartObject"));
so.getBoolean(stringIDToTypeID("linked"));
so.getString(stringIDToTypeID("fileReference"));
var missingLinkPath = so.getPath(stringIDToTypeID("link"));
var missingFile = missingLinkPath.toString().replace(/(^~.+\/)(.+)/, '$2');
// Relink the missing asset
var idplacedLayerRelinkToFile = stringIDToTypeID( "placedLayerRelinkToFile" );
var desc579 = new ActionDescriptor();
var idnull = stringIDToTypeID( "null" );
desc579.putPath( idnull, new File( theNewFolder + "/" + missingFile ) );
executeAction(idplacedLayerRelinkToFile, desc579, DialogModes.NO);
Copy link to clipboard
Copied
Thanks for the example! I use this option so rarely that I completely forgot to check. Indeed, with idforceNotify = false, the dialog with the relink offer does not appear!
My thoughts were directed specifically to using the standard batch (so that, in addition to relinking, when opening a file, other automation could be used), but it seems easier to add a batch processing module inside the script itself.
I'll definitely do this when I have some free time...
Copy link to clipboard
Copied
@jazz-y wrote:
Thanks for the example! I use this option so rarely that I completely forgot to check. Indeed, with idforceNotify = false, the dialog with the relink offer does not appear!
It must be beginner's luck! This is probably the first time that I have looked at this problem, so I had to attack it with no preconceptions.
My thoughts were directed specifically to using the standard batch (so that, in addition to relinking, when opening a file, other automation could be used), but it seems easier to add a batch processing module inside the script itself.
I'll definitely do this when I have some free time...
Please do so, I have been bashing away at this for a few hours now without any success.
I made a batch, but it only works with a single-layer file (or a file where the linked SO can be easily targeted). I have not had any success looping over all layers/sets and only processing a linked smart object, ignoring anything else.
/*
Batch relink missing SO layer to new folder.jsx
https://community.adobe.com/t5/photoshop-ecosystem-discussions/how-to-relink-files-in-multiple-photoshop-files/td-p/13011537
v1.0 - Stephen Marsh, 18th June 2022
*/
#target photoshop
// Select the broken links folder
var brokenLinkInput = Folder.selectDialog('Please select the broken links files directory:');
// Get the PSD & PSB files
var brokenLinkFiles = brokenLinkInput.getFiles(/\.(psd|psb)$/i);
// Select the folder to relink to
var theNewFolder = Folder.selectDialog('Please select the new directory to relink to:');
// Start the file processing counter at zero
var counter = 0;
for (var i = 0; i < brokenLinkFiles.length; i++) {
openSuppress(brokenLinkFiles[i]);
// TO DO: ADD CODE TO LOOP OVER ALL LAYERS & PROCESS ONLY LINKED SO LAYERS!
relinkPath();
activeDocument.close(SaveOptions.SAVECHANGES);
// Increment the file processing counter for each loop
counter++;
}
// End of script notification
app.beep();
alert('Script completed!' + '\r' + counter + ' files saved to:' + '\r' + brokenLinkInput.fsName);
/* Functions */
function openSuppress(theFiles) {
// Open the file suppressing the missing link dialog
var idopen = stringIDToTypeID("open");
var desc567 = new ActionDescriptor();
var iddontRecord = stringIDToTypeID("dontRecord");
desc567.putBoolean(iddontRecord, false);
var idforceNotify = stringIDToTypeID("forceNotify");
desc567.putBoolean(idforceNotify, false);
var idnull = stringIDToTypeID("null");
desc567.putPath(idnull, new File( theFiles ));
var iddocumentID = stringIDToTypeID("documentID");
desc567.putInteger(iddocumentID, 1081);
executeAction(idopen, desc567, DialogModes.NO);
}
function relinkPath() {
// Get the link path
/*
Based on:
https://stackoverflow.com/questions/63010107/get-a-smart-objects-layers-files-directory-source-in-jsx
*/
ref = new ActionReference();
ref.putProperty(charIDToTypeID("Prpr"), stringIDToTypeID("smartObject"));
ref.putEnumerated(charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt"));
var so = executeActionGet(ref).getObjectValue(stringIDToTypeID("smartObject"));
so.getBoolean(stringIDToTypeID("linked"));
so.getString(stringIDToTypeID("fileReference"));
var missingLinkPath = so.getPath(stringIDToTypeID("link"));
var missingFile = missingLinkPath.toString().replace(/(^~.+\/)(.+)/, '$2');
// Relink
var idplacedLayerRelinkToFile = stringIDToTypeID("placedLayerRelinkToFile");
var desc579 = new ActionDescriptor();
var idnull = stringIDToTypeID("null");
desc579.putPath(idnull, new File(theNewFolder + "/" + missingFile));
executeAction(idplacedLayerRelinkToFile, desc579, DialogModes.NO);
}
Copy link to clipboard
Copied
// TO DO: ADD CODE TO LOOP OVER ALL LAYERS & PROCESS ONLY LINKED SO LAYERS!
#target photoshop
s2t = stringIDToTypeID;
(r = new ActionReference()).putProperty(s2t('property'), p = s2t('numberOfLayers'));
r.putEnumerated(s2t('document'), s2t('ordinal'), s2t('targetEnum'));
var len = executeActionGet(r).getInteger(p),
linkedLayers = [];
for (var i = 1; i <= len; i++) {
(r = new ActionReference()).putProperty(s2t('property'), p = s2t('layerSection'));
r.putIndex(s2t('layer'), i);
if (typeIDToStringID(executeActionGet(r).getEnumerationValue(p)) == 'layerSectionEnd') continue;
(r = new ActionReference()).putProperty(s2t('property'), p = s2t('layerKind'));
r.putIndex(s2t('layer'), i);
if (executeActionGet(r).getInteger(p) == 5) {
(r = new ActionReference()).putProperty(s2t('property'), p = s2t('smartObject'));
r.putIndex(s2t('layer'), i);
if (executeActionGet(r).getObjectValue(p).getBoolean(s2t('linked'))) {
(r = new ActionReference()).putProperty(s2t('property'), p = s2t('layerID'));
r.putIndex(s2t('layer'), i);
linkedLayers.push(executeActionGet(r).getInteger(p))
}
};
}
alert('linkedLayersIDs:\n' + linkedLayers.toSource())
do {
(r = new ActionReference()).putIdentifier(s2t("layer"), linkedLayers.shift());
(d = new ActionDescriptor()).putReference(s2t("target"), r);
d.putBoolean(s2t('makeVisible'), false);
executeAction(s2t("select"), d, DialogModes.NO);
alert ('linked layer with id ' + d.getReference(s2t('target')).getIdentifier(s2t('layer')) +' selected!')
} while (linkedLayers.length)