I tried running the script and pointed it at the root folder of my music collection. it ran for a few seconds and then returned but nothing in the folder structure was changed.
am i running it correctly or is there something else i would need to do to get it to work correctly?
If you run the script on a folder containing subfolders containing jpeg files, the icon of the first jpeg file in a subfolder is given to this subfolder.
Isn’t it what was asked for ?
Yvan KOENIG (from FRANCE vendredi 10 novembre 2006 20:26:23)
Just stick around here for a while. In my opinion this is the number 1 spot to learn appescript. Look at some peoples scripts at the macscripters section, and look at code people have posted here. I have learned almost all my techniques from here. Don’t forget to practice, practice practice! Do small little test exercises to learn different techniques, of doing things. Good luck:)
As this set of commands doesn’t work if one used pathname contains accented chars, I coded a workaround:
the folder containing the source file is moved into the Temporary Folder and renamed.
The iconize process is done then the folder is moved back to it’s original folder and it is renamed with its original name.
As is, the coded workaround assumes that the jpeg filename doesn’t contain accented and make the same assumption upon the boot volume.
It’s what I am working with
-- [SCRIPT iconizer]
(* uses osxutils
with a workaround for accented chars
osxutils is available (free) from
http://osxutils.sourceforge.net/
*)
set _ to choose folder
open (_ as list)
on open (sel)
local F, cl
set F to (sel's item 1) as Unicode text
tell application "Finder"
if class of item F is document file then
my saveImageWithItselfAsIcon(F as alias) (* I'm not sure that the file already has an icon *)
else
if ((class of item F) is folder) then my iconizeFolder(F)
end if
end tell
end open
on iconizeFolder(F)
local ao, i, lesJPEGs, fichier
tell application "Finder" to tell folder F
set lesJPEGs to every item whose kind contains "jpeg"
if lesJPEGs is not {} then
set fichier to (lesJPEGs's item 1) as alias
my saveImageWithItselfAsIcon(fichier) (* I'm not sure that the file already has an icon *)
my copyIcon(fichier)
end if
repeat with i in (get folders)
my iconizeFolder((i as alias))
end repeat
end tell -- to folder F then to Finder
end iconizeFolder
on saveImageWithItselfAsIcon(F)
tell application "Image Events"
launch
tell (open F)
save with icon -- save icon_image_file with itself as icon
close
end tell
end tell
tell application "Finder" to update F
end saveImageWithItselfAsIcon
on copyIcon(F) (* F is an alias *)
local tName, F, coF, cocoF, nocoF, tempStorage, tempCof
tell application "Finder"
(* temporarily move and rename so the used pathname will not contain accented chars *)
set coF to (container of F) as alias
set {cocoF, nocoF, tName} to {(container of coF) as alias, name of coF, "osxutils_slituxso"}
set tempStorage to (path to temporary items from user domain) as alias
set coF to (move coF to tempStorage) as alias
set name of coF to tName
end tell -- to Finder
do shell script "/usr/local/bin/seticon " & quoted form of POSIX path of F & " " & quoted form of POSIX path of coF
tell application "Finder"
(* back to original storage and name *)
set coF to (move coF to cocoF) as alias
set name of coF to nocoF (* remet le vrai nom *)
update coF
end tell
end copyIcon
-- [/SCRIPT]
Yvan KOENIG (from FRANCE dimanche 12 novembre 2006 12:22:38)
beautiful applescript; was looking for this functionality in applescript, started writing my own but didn’t get very far due the lack of sufficient knowlegde
but i applied it to my music folder, in which every folder contains a jpg-file, but not every folder get an icon
i understand that the script takes the first jpg which the folder contains, but apparently there must be a difference somewhere
It seems that the Image Events application in macOS no longer understands AppleScript’s ‘alias’ specifier, which is what’s passed to the saveImageWithItselfAsIcon(F) handler in Yvan’s script. The simplest fix seems to be to change this line in the handler:
tell (open F)
to:
tell (open (F as «class furl»))
This coerces the alias to a different kind of file specifier that Image Events does understand and can use to open the image file.
The script in the post to which you’ve replied depends on a third-part utility called “osxutils” which has to be downloaded and installed before it can be used. The URL for it in the script’s comments now appears to be dead, which isn’t totally surprising after 17 years! Google returns several hits for the name, but I haven’t checked any of them out to see if they’re the same utility and work with current versions of macOS. The other scripts in this thread don’t need it.
Hi Nigel, and thanks for your help! The script I was trying to use was the one Yvan posted before the one requiring osxutils. I have amended the line that was causing the error, but it is still not producing the intended result (i.e pointing at a folder, if an image is present then use that image as the folder icon, any subfolders containing their own image use this image for the folder icon, any subfolders without an image file use the image in the parent folder as their icon). It doesn’t actually change the icon to the image expected, it just creates an icon of a generic ‘‘jpeg’’ icon; and any subfolders are ignored. Sometimes it will complete without an error, but if pointing the script to a folder containing several subfolders it will produce a time-out error.
As I say I am just at the start of my journey with AppleScript so it is all beyond me at present, but grateful for any advice you can give (as from the comments it seemed to work on older versions of MacOS).
I’ve run the script right through with a test folder this morning and it turns out that there’s been a change in the Finder’s behaviour too which essentially renders Yvan’s ingenious workaround useless. Also, the script was never intended to change the icon for folders not containing an image file. Its modus operandi is:
Search the current folder for any image files — specifically JPEG ones.
If there are any:
Use Image Events to open the first one found and to resave it immediately with the image it contains as its icon. This still works once the ‘alias’ business is sorted out.
Open a Finder information window for the image file and copy the icon from it to the clipboard. This still works, BUT the icon shown in the information window is now the generic one for an image file, not the actual icon assigned to the file. Or I should say: in many cases, the icon’s a generic one. On both my Ventura and Sequoia machines, my testing so far has shown that only image file icons are genericised (if that’s a word) and only some image files are so affected. I’ve not so far been able to determine what sets affected and unaffected image files apart. It doesn’t seem to be related to size, age, colourisation, or dimensions.
Open a Finder information window for the current folder and paste the copied icon image into it. This sets the icon for the folder. (An invisible file whose name is (“Icon” & return) is saved in the folder.)
Recursively repeat the above with any subfolders, subfolders of subfolders, etc.
So I don’t know the answer at the moment. I’ll look into it further, and hopefully others will be inspired to do so too, but for now I’m stumped.
Yes. It seems to. When I apply your script to my test hierarchy, which contains one or two subfolders that don’t contain JPEG files, it complains that the folder is empty and only offers the option to stop. None of the folders’ icons are changed. It does work though with a folder containing a JPEG and no subfolders. And using ASObjC to handle the icons is definitely the way to go!
Thanks Nigel for your explanation. I misunderstood the OP’s requirements, confusing the folder selected by the user with the parent folder. This raises the question as to what should happen if a subfolder does not have a JPG and that subfolder’s parent folder does not have a JPG. Do you just keep going up the path until you find a JPG?
As I understand it from rob72’s description above (I expect he’ll clarify it himself later), he wants every folder in the chosen hierarchy that contains a JPEG file to have an icon that’s the same picture as the one in that file. If a folder doesn’t contain a JPEG file, he wants that folder to have the same icon as the one given to the chosen root folder. Yvan’s scripts do something similar except that folders not containing JPEGS aren’t given new icons. It may be a good idea to wait for rob72 to confirm what he wants before getting too deeply into the code development! I won’t be able to do anything about it myself for the next day or so, so I’ll leave it to your expertise with NSIcon methods!
Hi Nigel, that is exactly what I’m hoping to achieve, thank you. In the case that a subfolder does not have an image file (it could be a jpg or a png) my goal is that the subfolder inherits the same icon image as its parent folder.
Thanks Nigel. I’ve included below a script that works in the above fashion. Please note that the user is notified of an error and the script stops if the chosen root folder does not contain a JPG file. I tested this script on my Sequoia computer without issue.
--revised 2025.01.22
use framework "AppKit"
use framework "Foundation"
use scripting additions
on main()
set allowedSubfolders to 10 --set to desired value
set theFolder to POSIX path of (choose folder)
set theFolder to current application's |NSURL|'s fileURLWithPath:theFolder
set folderImage to getImage(theFolder)
if (folderImage's |count|()) is 0 then display dialog "This script cannot proceed because an image file was not found in the chosen root folder." buttons {"OK"} cancel button 1 default button 1
set folderImage to folderImage's objectAtIndex:0
set theSubfolders to getSubfolders(theFolder)
set subfolderCount to theSubfolders's |count|()
if subfolderCount is greater than allowedSubfolders then display dialog "This script cannot proceed because the chosen root folder contains " & subfolderCount & " subfolders, which exceeds the allowed number of subfolders." buttons {"OK"} cancel button 1 default button 1
display dialog "The chosen root folder contains " & subfolderCount & " subfolders. Do you want to change the icon of the chosen root folder and all of its subfolders? An existing Finder window may have to be closed and reopened to see the changed icons." cancel button 1 default button 1
set folderImage to (current application's NSImage's alloc()'s initWithContentsOfURL:folderImage)
(current application's NSWorkspace's sharedWorkspace()'s setIcon:folderImage forFile:(theFolder's |path|()) options:2) --option 2 suppresses QuickDraw format icons
repeat with aSubfolder in theSubfolders
set subfolderImage to getImage(aSubfolder)
if (subfolderImage's |count|()) is 0 then --image file not found in subfolder
set subfolderImage to folderImage
else
set subfolderImage to (current application's NSImage's alloc()'s initWithContentsOfURL:(subfolderImage's objectAtIndex:0))
end if
(current application's NSWorkspace's sharedWorkspace()'s setIcon:subfolderImage forFile:(aSubfolder's |path|()) options:2)
end repeat
end main
on getSubfolders(theFolder)
set fileManager to current application's NSFileManager's defaultManager()
set folderKey to current application's NSURLIsDirectoryKey
set packageKey to current application's NSURLIsPackageKey
set booleanTrue to current application's NSNumber's numberWithBool:true
set folderContents to (fileManager's enumeratorAtURL:theFolder includingPropertiesForKeys:{} options:6 errorHandler:(missing value))'s allObjects() --option 6 skips hidden files and package descendants
set theSubfolders to current application's NSMutableArray's new()
repeat with anItem in folderContents
set {theResult, aFolder} to (anItem's getResourceValue:(reference) forKey:folderKey |error|:(missing value))
if aFolder is booleanTrue then
set {theResult, aPackage} to (anItem's getResourceValue:(reference) forKey:packageKey |error|:(missing value))
if aPackage is not booleanTrue then (theSubfolders's addObject:anItem)
end if
end repeat
return theSubfolders
end getSubfolders
on getImage(theFolder)
set fileManager to current application's NSFileManager's defaultManager()
set folderContents to fileManager's contentsOfDirectoryAtURL:(theFolder) includingPropertiesForKeys:{} options:4 |error|:(missing value) --option 4 skips hidden files
set thePredicate to current application's NSPredicate's predicateWithFormat_("pathExtension.lowercaseString IN %@", {"jpg", "jpeg", "png"}) --set desired image formats which are case-insensitive
return (folderContents's filteredArrayUsingPredicate:thePredicate)
end getImage
main()