Loop through every photo in Photos - macos

I want to clean up my Photos library and especially move all my iPhone screenshots to a new album, that way I can easily look through them and delete what I want.
Now for this I first wanted to make a smart album in Photos, but it seems that Photos can't filter on the dimensions of an image, so I started up the AppleScript Editor :).
I created the following script, which works on a "Test Album" I created:
tell application "Photos"
set source_album_name to "Test Album"
set target_album_name to "Screenshots"
set target_width to 640
set target_height to 1136
if not (exists container named target_album_name) then
make new album named target_album_name
end if
set target_album to container target_album_name
set source_album to container source_album_name
set imageList to {}
repeat with photo in media items in source_album
if width of photo = target_width and height of photo = target_height then
set the end of imageList to photo
end if
end repeat
add imageList to target_album
end tell
This script loops through an Album named Test Album and compares the height and width to the dimensions of an iPhone 5S. When they match, it adds the photo to the Screenshots library. No problems there.
Now I want to run this script on my entire photo collection, so I changed the line repeat with photo in media items in source_album to repeat with photo in every media item.
This generates an error once it is past the first item (Photos got an error: Can’t get item 2 of every media item. Invalid index).
After that I changed the code to:
set all_images to every media item
repeat with photo in all_images
but after loading for a while, the script exits with code -10000, probably because of the amount of photos in that library (27.000).
Is there some way to page through a set like this?
EDIT: Changing the set line to contain a better query has the same effect, resulting in an AppleEvent handler failed with error number -10000.
set all_images to every media item whose width = target_width and height = target_height

The -10000 error is due to a bug in Photos 1.0. I've filed this with Apple as radar 20626449 (on OpenRadar at http://openradar.appspot.com/radar?id=6090497735000064). The error is intermittent, but the more photos there are in the library, the more likely it is to occur on any given scripting command.
There is no totally reliable way around the error, but one thing that seems to help is if the "All Photos" album is selected in Photos before starting your script. Unfortunately the Photos AppleScript suite doesn't have the ability to select an album, so you'll need to do this manually before starting the script.

Not sure if you mind dropping into the shell in Terminal, or if you can make that work with iPhoto, but if that is ok, this may get you started on finding all files in your HOME directory and below, that are PNGs and that match your sizes...
#!/bin/bash
isIphone() {
# Get width using sips, don't bother getting height if not 640 wide
w=$(sips -g pixelWidth "$1" | awk 'END{print $2}')
[ "$w" != "640" ] && return
# Get height using sips, just return if not iPhone size
h=$(sips -g pixelHeight "$1" | awk 'END{print $2}')
[ $h != "1136" ] && return
# Output image size and name
echo $w,$h, $1
}
# Go through all PNG files in HOME directory listing iPhone-sized ones
find ~ -iname "*.png" -type f -print0 | while read -d $'\0' -r file ; do isIphone "$file"; done
Output
640,1136, ./iPhone/2013 09 iPhone/IMG_0269.PNG
640,1136, ./iPhone/2013 09 iPhone/IMG_0363.PNG
640,1136, ./iPhone/2013 09 iPhone/IMG_0376.PNG

Related

Automator Move Preview/Thumb image to Folder with similar FileName as the image (Applescript)

I GET 2 ERRORS.
OBJECTIVE: In my downloads folder I have:
FOLDER: fileName 001
IMAGE: fileName 001 preview.jpg
FOLDER: fileName 002
IMAGE: fileName 002 preview.jpg
FOLDER: fileName 003
IMAGE: fileName 003 preview.jpg
I want Automator to move all previews inside their respective folders.
So, I followed this similar tutorial and I got this so far:
1st)
on run {input, parameters}
return input
end run
2nd) Filter Finder Items: Kind is Folder
3rd) Set Value of Variable: FilePath ( path where image has to go )
4th) IN THIS STEP I GET AN ERROR: Check the actions properties ( I'm new so don't know ).
on run {input, parameters}
tell application "Finder"
set FileName to name of file input
end tell
end run
5th) Set Value of Variable: FilePath ( path where image has to go )
6th) Filter Finder Items: Kind is Image AND Name contains {FilePath} ( same path as folder name ).
The problem occurs with the {FilePath}, Automator doesn't accept the newly created variable: FilePath, in the contains field, the Newly created variable called FilePath.
7th) Get Value of Variable: FileName
8th) Move Finder Items to: FilePath
Here is the workFlow file.
The link to your workflow file didn't work, but it's ok—thanks for trying to share it anyway, but, in this instance, I can work without it, as I'm actually going to do it from scratch:
Depending how your workflow will be setup to receive the input, you can remove the Get Specified Finder Items action and allow the workflow to run as a Service, for example, whose input will be the folder that contains images to be processed. As you said it was your Downloads folder that contains the images, I felt it was unlikely that you'd be setting up the workflow to work as a Folder Action.
For testing purposes, you can keep the Get Specified Finder Items action and replace the Example folder I've put in there with your Downloads folder.
Here's the code from the Run AppleScript action for you to copy/paste:
on run {input, parameters}
# input will be the folder containing the images, e.g. the Downloads folder
set [here] to the input
set suffix to "preview.jpg"
set my text item delimiters to {pi, space & suffix}
tell application "Finder"
set jpgs to the name of every file in here whose name ends with the suffix
repeat with jpg in jpgs
set this to the text items of jpg as text
set there to (a reference to the folder named this in here)
if not (there exists) then set there ¬
to make new folder in here ¬
with properties {name:this}
move the file named jpg in here to there
end repeat
end tell
end run
It matters not whether the destination folders exist: if they do, the image will be moved into the appropriate one; for those that don't, the folder is created before the image is moved.
Update In Response To Comments
① Sometimes I have a string between "preview" and extension .jpg or .png
② I'd like to also move the completed folders to a new folder /Users/sebba/BLENDER/BLENDS
Given the variability of your filenames that you've now disclosed, I think it's easier to use a shell script instead of an AppleScript to process the files.
Delete the Run AppleScript action and insert a Run Shell Script action, with the following options: Shell: bin/bash, Pass input: as arguments.
Delete any sample code that appears in the editing field and enter this code:
cd "$1"
folder=()
while read -r file
do
folder+=("$(egrep -io '^.+ \d{3}' <<<"$file")")
[[ -d "${folder[#]: -1}" ]] || mkdir "${folder[#]: -1}"
mv -v "$file" "${folder[#]: -1}"
done <<<"$( ls | egrep -i '^.+ \d{3} preview.*\.(jpg|png)' )"
mv -v "${folder[#]}" "/Users/sebba/BLENDER/BLENDS"
This uses a regular expression match to pick out filenames that:
Begin with absolutely anything (but not nothing); then
Follow with a space, precisely three digits, a space, then the word "preview"; then
Follow with absolutey anything (including nothing); then
End with ".jpg" or ".png".
Therefore, it will move the following files to the given folder (the folder is created if it doesn't exist, and files with the same name are overwritten):
My image_file 001 preview.foo bar.jpg → My image_file 001/
004 2 001 preview.png → 004 2 001/
But the following files won't be affected:
001 preview.png
My image_file 01 preview.jpg

Retina display: How to display images in native pixel resolution in Finder and Preview

This is a pseudo question to share my own trick and script below.
The point is to be able to display image pixel for pixel on Retina displays. This is mainly useful for high resolution image and/or for developers working on HDPI version of images.
The solution works well only if the display setting is set to 2:1 ratio in the OS X preferences. Beware, late 2016 MacBook Pro default setting is not set to 2:1 by default. You should set it on the medium setting to get it right.
Finder : the simple trick is to give a name ending with #2x (before the extension): my_image#2x.jpg . Then when using Quick Look feature the image is pixel-wise. As this naming scheme is recommended for retina images, both normal and HDPI images display at same size, as expected, the retina being sharper.
Preview : In preview, the DPI resolution of an image is interpreted as normal if it is set to 72dpi. By setting it to 144, you get the right display ratio. One can achieve the same effect at 72dpi by changing the display scale to 50%, but the scale setting does not stick to the image file while the DPI setting does. Change it through the Tools->Size menu item.
Here below is a small applescript to automate 144dpi setting from the Finder.
tell application "Finder"
repeat with item_cour in selection as list
if word 1 of (the kind of item_cour as text) is "Image" then
set path_cour to POSIX path of (item_cour as text)
do shell script "p_cour='" & path_cour & "';
tags=$(xattr -px com.apple.metadata:_kMDItemUserTags \"$p_cour\");
f_info=$(xattr -px com.apple.FinderInfo \"$p_cour\");
sips -s dpiHeight 144 -s dpiWidth 144 \"$p_cour\";
xattr -wx com.apple.FinderInfo '$f_info' \"$p_cour\";
xattr -wx com.apple.metadata:_kMDItemUserTags \"$tags\" \"$p_cour\" "
-- do shell script "convert \"" & path_cour & "\" -set units PixelsPerInch -density 144 \"" & path_cour & "\""
end if
end repeat
end tell
Since the sips command does not preserve tags, the script includes 4 lines to get and set them back to the file after it has been modified, using the xattr command.
To install the script: open the script editor, create a new document, paste the code and save it into the ~/Library/Scripts/Finder folder.
Be sure to check the Show the Script Menu option in Script editor preference.
To use the script: select image file(s) in Finder and activate the script from the menu.

shell script to create libre office Impress with images

I've some image files generated by an analysis. Every time I do analysis, the file names are same. And I've to create a presentation, and I'm using Libre Office Impress.
Let us say, I've three images image1.png, image2.png, and image3.png, and I should put these images say in page no 3, page no 5, and page no 8
Now, I'm inserting images manually. I know very basic shell scripting. So I was wondering what would be the bash shell script to automatically create an libre office impress file with images automatically inserted at pages mentioned as above.
If the main part of the .odp file doesn't change, it is possible to make a template in flat openDocument format, where the names of the 3 images are updated by script.
A flat document is a uncompressed xml document that can be open with a text editor to change some part manually with caution. Usually the name of such a document is rather with the .fodp You must save this template in flat format, with links to the images rather than incorporate them.
So.
Let us say :
the template is at /path/to/the/template.fodp beside the images im1.png, img2.png, img3.png of the example.
the name of the first image is image1.png, the second image2.png, and the third : image3.png.
the images to be imported are in the same directory /path/to/the/document where will be the final document.
Let us write a script insertImages.sh
myTpl="$1" # will contains '/path/to/the/template.fodp'
myDir="$2" # will contains '/path/to/the/document'
img1="$3" # will contains the name of the first image in myDir
img2="$4" # will contains the name of the second image in myDir
img3="$5" # will contains the name of the third image in myDir
[[ -f "$1" ]] && cp "$1" "$2/document.fodp" || exit 1 # checks if the template exists and copy it
[[ -f "$3" ]] && sed "$2/document.fodp" "s/img1.png/$3/" || exit 1 # overwrite the name of the first image
[[ -f "$4" ]] && sed "$2/document.fodp" "s/img2.png/$4/" || exit 1 # overwrite the name of the second image
[[ -f "$5" ]] && sed "$2/document.fodp" "s/img3.png/$5/" || exit 1 # overwrite the name of the third image
This script should be invoked this way :
insertImages.sh "/path/to/the/template.fodp" "/path/to/the/document" "image1.png" "image2.png" "image3.png"
I'm not a big programmer. So there is probably some mistakes in those lines. But the principle is there.
They are some constaints :
a flat document
relative pathes to the images.

Applescript, show all files with tag

I am trying to build an applescript which will open show all files tagged with the school class I am currently in, based on current time. For instance, if the time is 10 AM on a Tuesday, it will show all my files tagged with Chemistry. Now, I have made the AppleScript to get the correct name of the class.
Now I need to tell Finder to open the files with the correct tags. How would I do this?
Basicaly, something like this:
set tagged to "Tag:Chemistry"
tell application "Finder"
reveal tagged
activate
end tell
Ofcourse, tagged here would be dynamically assigned.
The way way I would do this in the GUI is with Smart Folders. Go in the Finder, click on File->New Smart Folder and a new Finder window appears. Click on + at the top right beside Save and change Kind at top left of window to Tags. Beside Tags choose contains and then type Chemistry. Click Save at top right and call it Chemistry Stuff and allow it to be added to the side-bar. Then it will appear on the left of all Finder windows.
In the shell/Terminal, where I usually reside, I use tag for that purpose. I installed it with homebrew using brew install tag. Then I can do tag -f sometag and it lists all the files that are tagged sometag.
tag - A tool for manipulating and querying file tags.
usage:
tag -a | --add <tags> <file>... Add tags to file
tag -r | --remove <tags> <file>... Remove tags from file
tag -s | --set <tags> <file>... Set tags on file
tag -m | --match <tags> <file>... Display files with matching tags
tag -l | --list <file>... List the tags on file
tag -f | --find <tags> Find all files with tags
<tags> is a comma-separated list of tag names; use * to match/find any tag.
additional options:
-v | --version Display version
-h | --help Display this help
-n | --name Turn on filename display in output (default)
-N | --no-name Turn off filename display in output (list, find, match)
-t | --tags Turn on tags display in output (find, match)
-T | --no-tags Turn off tags display in output (list)
-g | --garrulous Display tags each on own line (list, find, match)
-G | --no-garrulous Display tags comma-separated after filename (default)
-H | --home Find tagged files only in user home directory
-L | --local Find tagged files only in home + local filesystems (default)
-R | --network Find tagged files in home + local + network filesystems
-0 | --nul Terminate lines with NUL (\0) for use with xargs -0
You can use the GUI Scripting to click on left-pane in the finder window.
Because this script use GUI Scripting to control the user-interface, you must add the applet to an approval list, displayed in the Security & Privacy system preference pane.
set tagName to "Chemistry" -- the full tag's name
my showFinderWindow(tagName)
on showFinderWindow(thisTag)
tell application "Finder" to if not (exists Finder window 1) then make new Finder window
tell application "System Events"
tell process "Finder"
repeat with i in windows
tell outline 1 of scroll area 1 of splitter group 1 of i to if exists then
tell (first row whose value of its static text 1 is thisTag) to if exists then perform action "AXOpen" of static text 1
exit repeat
end if
end repeat
end tell
end tell
end showFinderWindow
You can use the command line tool "mdfind" to search for tagged files using "kMDItemUserTags". So that's how I would find the tagged files.
The other part of your problem is finding the proper tag for the current time-of-day. I would use applescript records for this. For example you can make a record like this:
{theTag:"Chemistry", startTime:date ("10:00 AM"), endTime:date ("11:00 AM")}
So if you make a list of records you can loop through them and compare the start and end times of each record in the list to the current date.
Finally, once you have that and assuming you found some files with mdfind then you can just loop through the found files and ask the Finder to open them.
Try this. Note you have to supply your values for "requirementsRecords" and "searchFolder" in the user variables section.
-- user variables
set requirementsRecords to {{theTag:"Chemistry", startTime:date ("10:00 AM"), endTime:date ("11:00 AM")}, {theTag:"Math", startTime:date ("11:00 AM"), endTime:date ("12:00 PM")}}
set searchFolder to path to desktop
-- find the tag information given the current time
set now to current date
set thisTag to missing value
repeat with i from 1 to count of requirementsRecords
set thisRecord to item i of requirementsRecords
set thisStartTime to startTime of thisRecord
set thisEndTime to endTime of thisRecord
if now is greater than or equal to thisStartTime and now is less than or equal to thisEndTime then
set thisTag to theTag of thisRecord
exit repeat
end if
end repeat
if thisTag is missing value then error "Could not find a tag for the current time!"
-- search the search folder for files with the tag
set cmd to "mdfind -onlyin " & quoted form of POSIX path of searchFolder & " \"kMDItemUserTags == " & quoted form of thisTag & "\""
set resultsList to paragraphs of (do shell script cmd)
if resultsList is {} then error "Could not find any files tagged \"" & thisTag & "\" in the search folder"
-- open the found files with the Finder
repeat with anItem in resultsList
set thisFile to POSIX file anItem
tell application "Finder" to open thisFile
end repeat

How to sort images by width in Applescript?

I would like to sort my pictures by size, ad it is easiest to do so by width. I would like to get images with a width larger than 1919px and put it in another folder. I have googled and tried things for hours with no luck.
I've been getting this error: error "Image Events got an error: Can’t make item 1 of dimensions of image \"1mouwi.jpg\" into type specifier." number -1700 from item 1 of dimensions of image "1mouwi.jpg" to specifier at item 1 in the repeat loop. any help on how to fix this?
My Code:
set picFolder to alias ":Users:USERNAME:Pictures:DESKTOPS:"
set hdFolder to alias ":Users:USERNAME:Pictures:DESKTOPS_HD:"
tell application "System Events"
set photos1 to path of files of picFolder
set num to count of photos1
set photos to items 2 thru num of photos1
end tell
set hd to {}
repeat with imgPath in photos
set imgAlias to alias imgPath
tell application "Image Events"
set img to open imgPath
set width to item 1 of dimensions of img
if width > 1919.0 then
set end of hd to imgAlias
end if
close img
end tell
end repeat
tell application "Finder"
move hd to hdFolder
end tell
A simple solution is to use the "get" command and parenthesis. In general "get" is understood so you normally don't have to use it in commands... but a quirk in applescript requires you to explicitly use it sometimes... especially when you have multiple commands in one line. Also the parenthesis ensure the multiple operations of the width line of code are performed in the right order
set width to item 1 of (get dimensions of img)
However there's a few other optimizations you could use so here's how I would write your script.
set picFolder to (path to pictures folder as text) & "DESKTOPS:"
set hdFolder to (path to pictures folder as text) & "DESKTOPS_HD:"
tell application "System Events" to set photos1 to path of files of folder picFolder
set photos to items 2 thru end of photos1
set hd to {}
repeat with imgPath in photos
set imgAlias to alias imgPath
tell application "Image Events"
set img to open imgAlias
set width to item 1 of (get dimensions of img)
close img
end tell
if width > 1919 then
set end of hd to imgAlias
end if
end repeat
tell application "Finder"
if hd is not {} then move hd to folder hdFolder
end tell
You might just use mdls -rn kMDItemPixelWidth in the shell:
for f in ~/Pictures/DESKTOPS/*; do [[ $(mdls -rn kMDItemPixelWidth "$f") -ge 1920 ]] && mv "$f" ~/Pictures/DESKTOPS_HD/; done
If mdls doesn't show the sizes of some images, try using ImageMagick or sips:
brew install imagemagick; identify -format %w input.png
sips --getProperty pixelWidth input.png | awk 'END{print $NF}'
It works if you split up the dimension and width into two separate lines.
tell application "Image Events"
set img to open imgPath
set dim to dimensions of img
set width to item 1 of dim
[...]
end

Resources