Opening Randomized Images From Multiple Folders With Preview Application
Fixing and rewriting the code, particularly in the repeat with aFolder in theFolders
loop, and using a handler from the accepted answer by Lri in Is there a simple way to shuffle a List in AppleScript? to randomize the list over your method, this now works.
I tested it on several different folders individually as well as multiple folders together, with as many as 500 JPG files in a test run.
tell application "Finder"
if not selection is {} then
set theFolders to selection as alias list
else
return
end if
end tell
set theImages to {}
repeat with aFolder in theFolders
set getImages to "find " & aFolder's POSIX path's quoted form & " -iname '*.jpg'"
set tempList to paragraphs of (do shell script getImages)
repeat with aItem in tempList
copy aItem to end of theImages
end repeat
end repeat
set randomImages to shuffle(theImages)
set filePaths to {}
repeat with thisFile in randomImages
copy (thisFile as POSIX file as alias) to the end of filePaths
end repeat
tell application "Finder" to ¬
open filePaths using application file id "com.apple.Preview"
### Handlers ###
on shuffle(input)
script s
property L : input
end script
set i to count of L of s
repeat while i ≥ 2
set j to random number from 1 to i
set {item i of L of s, item j of L of s} to {item j of L of s, item i of L of s}
set i to i - 1
end repeat
L of s
end shuffle
Looks like I'm a bit late to the party, but here's another way to do it. I agree with you about Finder being slow (sometimes because it's not being used in the right way, but you've done everything right). It shouldn't be used for any filesystem operations other than ones that are unique to it (reveal
, select
, update
) or for accessing properties and objects that are only available through Finder (selection
, Finder window
, original item
, application file
). It was actually superseded years ago by System Events, which took over it's filesystem and process inspection capabilities, but a lot of AppleScripters are stuck in their ways. System Events isn't quite as fast as the shell, but you ought not to notice an appreciable difference unless you're handling thousands of files at once. It is, however, approximately infinity times faster than Finder.
Firstly, I defined a script object just to house a couple of handlers and to improve speed for accessing list items:
script filepaths
property selection : {}
property list : {}
to flatten(L)
script
property parent : L
property index : 1
on |ƒ|()
set x to {} & item index
if x = {} then return
set [[item index], x] to ¬
[it, rest] of x
if x = {} then return
set the end to x
end |ƒ|
end script
tell result to repeat until index > length
|ƒ|()
set index to index + 1
end repeat
L
end flatten
to shuffle(L)
script
property parent : L
property index : my items
end script
tell result
repeat with i from 1 to length
set item i in the index to i
end repeat
repeat with i from 1 to length
set k to some item in the index
set [item k, item i] to ¬
[item i, item k]
end repeat
end tell
L
end shuffle
to open
tell application id "com.apple.Finder" to open ¬
my list using application file id ¬
"com.apple.Preview"
end open
end script
There's nothing hugely complicated there. The flatten()
handler will take a list of nested objects and return a flattened list. The shuffle()
handler will shuffle a list in a reliably pseudorandom way that ensures every possible permutation for a list had an equal chance of arising. Some pseudorandom number generators have a slight tendency to favour items that don't sit at the boundaries.
That script object can go anywhere. Then the main part of the script looks like this:
tell application id "com.apple.Finder" to set the ¬
selection of filepaths to the selection as ¬
alias list
tell application id "com.apple.SystemEvents" to repeat with f in ¬
(a reference to the selection of filepaths)
if f's kind = "Folder" then set f's contents ¬
to (the path of the files in f where ¬
name extension = "jpg" and visible = true)
end repeat
tell filepaths
set its list to every list in the selection
flatten(its list)
shuffle(its list)
open
end tell
It uses Finder only to grab the selected files; it uses System Events to filter the selection so that only folders are considered, and then it reads the contents of those folders and mutates the list to contain the jpg
files in those folders.
This will be a nested list, which would be fine to open in Preview without further processing, but in order to shuffle it, it first needs to be flattened. So that's done, then the order is randomised, then they're opened in Preview.
I tested this with a few hundred images in a dozen folders, and it was blindingly fast up until I stupidly opened them all in Preview, at which point I regretted that. Hopefully you have a more powerful machine than I, because mine is now roasting hot and requires a restart.