Showing posts with label applescript. Show all posts
Showing posts with label applescript. Show all posts

Sunday, October 29, 2023

Scripting Photos.app in Ventura (and maybe Sonoma) with AppleScript, Automator, and Shortcuts

[This post will be gradually developed. At the moment search engines return almost nothing on the current mess topic so even in its initial state it should be helpful.]

I recently moved my Aperture Photo Library from a machine running Mojave (last version OS with Aperture support) to one running Ventura and Photos.app. The migration was unspeakable.

Under Monterey there were a number of AppleScripts that mitigated key missing features in Photos (like batch rename). Some seem to ship from Apple with the OS. Others could be found online. Several appear in a compendium of user tips and scripts from Apple Discussions. But things have changed since Mojave and Monterey...

This blog post is going to be about scripting in Photos.app for Ventura and Monterey. In my own experimentation I was able to convert a 2019 Automator.app script containing AppleScript (basically it's a wrapper around a traditional AppleScript) into a Shortcuts.app script that runs as a service within Photos.app. To do this I had to enable AppleScript for use with Shortcuts and in Shortcuts details I could add it to the services menu. (Howard Oakley has an overview of the AppleScript to Shortcuts transition and an earlier article on Shortcuts in particular. The Shortcuts user guide article on this seems to have come from Automator but does actually work.)

I wasn't able to get the directions for adding Automator Quick Action Workflows in the Ventura Automator User Guide to work.

So I can batch rename images now from an entry in the Services menu. Yay.

If I find a good repository of Shortcuts I'll link to that. Otherwise I will try hosting them on my personal site or GitHub. I have an Apple Discussions question on the topic.



Sunday, October 16, 2022

AppleScripts to speed Java compile and execute with BBEdit

Students are often asked not to use an IDE (I like Visual Studio Java) for their Java projects. Instead they need to use BBEdit without an IDE. BBEdit does not appear to natively support facilitated compile/execute but it does run AppleScrips that can speed things a bit.

The davalign.com site has some scripts written for TextWranger. If you rename them to BBEdit they work well.

Reproducing them here in case that site vanishes. I didn't see much like this. Note if you open and save in macOS Script Editor they will be compiled. I like that the first action is to save the document, it's easy to forget to save before a compile.

Compile java.scpt

tell application "BBEdit"
save text document 1
set the_file to file of text document 1
end tell

set AppleScript's text item delimiters to ":"
set source_file to the last text item of (the_file as string)

tell application "Finder"
set the_folder to container of file the_file
end tell

tell application "Terminal"
activate
set p to POSIX path of (the_folder as string)
set shell_script to "cd " & (quoted form of p) & ¬
"; javac " & source_file
if (count windows) is 0 then
do script shell_script
else
do script shell_script in the front window
end if
end tell

Run java.scpt

tell application "BBEdit"
set the_file to file of text document 1
end tell

set AppleScript's text item delimiters to ":"
set source_file to the last text item of (the_file as string)
set compiled_file to text 1 thru -6 of source_file

tell application "Finder"
set the_folder to container of file the_file
end tell

tell application "Terminal"
activate
set p to POSIX path of (the_folder as string)
set shell_script to "cd " & (quoted form of p) & ¬
"; java " & compiled_file
if (count windows) is 0 then
do script shell_script
else
do script shell_script in the front window
end if
end tell

Monday, February 15, 2021

Big Sur bug: Mail Search (corespotlightd) fails on multi-user machine after a user logs out

We are indebted to GanawaGangunawa for figuring out why Mail search was failing on Emily's M1 MacBook Air (known to our family as "Crashy" [1]). It's a Big Sur bug (though I think it happened in some Catalina environments) that hits multi-user machines.

In our case Ben and Emily both have non-admin accounts on her M1 Air running Big Sur 11.2.1 with fast user switching enabled. When Ben logs out Emily's Mail search stops working. There's no error message, but search does nothing and Smart Folders are inactive.

The fix is to kill corespotlightd.

I created an AppleScript with the contents: 

do shell script "killall -9 corespotlightd"

I saved it as an application and put it on Emily's dock. Two clicks fixes her Mail search until Apple fixes the bug.

[1] When we first got the M1 Air it crashed (spontaneous restart) every few hours. Reinstalling Big Sur meant it crashed every few days, with Big Sur 11.2 it didn't crash, with 11.2.1 it restarts every week or so. I suspect a firmware/OS mismatch in the factory was the initial problem and that for the rest that Big Sur/M1 are not quite stable yet. I almost returned Crashy in the 2 week return window but it seems just stable enough. Good chance future OS updates will fix. It does pass hardware test.

Thursday, November 05, 2015

Thunderbolt Dock: Eject all disks prior to undock

My new Elgato TB2 dock comes with an installer for an undock utility, but it also installs a kernel extension for some other function. I need a kernel extension like I need a meth habit.

So I was looking at 3rd party Mac App Store solutions like Mountain.app when @clackgoble on app.net said to just do AppleScript. Google found one then I added Clark’s eject line. I saved it as “Undock.app” and I launch by Spotlight (Cmd-spacebar “und”).

FWIW:

-- http://irwinkwan.com/2013/06/27/eject-all-mac-os-x-disks-with-a-script/

try

tell application "Finder"

-- Original: eject the disks

-- Clark Goble version:

eject (every disk whose ejectable is true and local volume is true and free space is not equal to 0)

display dialog "Successfully ejected disks." buttons {"Close"} default button "Close"

end tell

on error

display dialog "Unable to eject all disks." buttons {"Close"} default button "Close"

end try

Update 7/23/2016

The above version may not be reliable in El Capitan (presumably an OS bug). I’m told this works:

tell application "Finder" to eject (every disk whose ejectable is true and local volume is true)

Sunday, August 04, 2013

Using Automator to create a plaintext list of upcoming events from OS X Calendar.app

This isn't the Using AppleScript to create a plaintext list of upcoming events from OS X Calendar.app solution I want (much less something built into Calendar.app), and it's not necessarily better than How to get a reasonable plaintext listing of calendar events using Google Calendar or OS X Calendar.app, but this Automator solution was pretty easy to put together.

It's not so easy to share though, one of the weird things about Automator is how hard it is to share a set of steps. Here's a screenshot that gives the gist of it:

Screen Shot 2013 08 04 at 5 02 05 PM

The output is clunky, but I've seen worse:

...

EVENT 16 OF 18
Summary: bdfafdf
Status: none
Date: 8/24/13 to 8/25/13
Time: 12:00:00 AM to 12:00:00 AM
Location:
Notes:

...

See also:

Using AppleScript and/or Python to create a plaintext list of upcoming events from OS X Calendar.app

[This post starts with the general problem and some basic scripts -- but don't worry -- it ends with two definitive solutions.]

Yesterday I described several clumsy hacks to get a plaintext display of upcoming Calendar events from one or more Mac or Google Calendars. In today's post I look at what's possible with AppleScript [1] (or Python and AppScript) support -- though as of 8/4/13 I'm not done with it.

Specifically I want to get a plaintext list of events on one or more Google Calendars through AppleScript and Calendar.app when Calendar.app is configured to read Calendars from a Google share. Based on some articles I found, it looks possible:
I was able to retrieve the references and names of all my Google Calendars using 2005 code from MacTech

tell application "iCal"
set theCalendars to every calendar
end tell
and
tell application "iCal"
set theCalendarNames to title of every calendar
end tell

I was also able to view a calendar at a specified date and I found it used the selected calendars though not in my desired format.

This script listed events by event id for the current date and it seems easy to modify if I study some AppleScript date arithmetic ...

set {year:y, month:m, day:d} to current date
set str to (m as string) & " " & (d as string) & " " & (y as string)
set today to date str
set tomorrow to today + 60 * 60 * 24
tell application "iCal"
tell calendar "Lotus Notes"
set curr to every event whose start date is greater than or equal to today ¬
and start date is less than or equal to tomorrow
end tell
end tell

This one worked, but search can take a very long time...
tell application "iCal"
tell calendar "Domestic"
set theEventList to every event whose summary contains "Recycling"
end tell
set theEvent to first item of theEventList
return summary of theEvent
end tell

I'll have to leave this task for a bit, but I have a long family car drive coming up and I might be able to play with it on the way. I've also asked about this on MacScripter.net, where respondents have been known to solve problems very thoroughly.

Or I could just try Automator...Using Automator to create a plaintext list of upcoming events from OS X Calendar.app

- fn -

[1] SQL, AppleScript and COBOL all have one thing in common - they were designed for use by "non-programmers". Let that be a lesson.

See also
Update 8/5/13: Nigel Garvey, a moderator at the extraordinary and venerable MacScripter site, did a professional version of this script. It's working quite well for me, though it only works with one Calendar. It's easy to tweak the text result. (See post for his comments.)

-- Ask the user for the range of dates to be covered.on getDateRange()
   set today to (current date)
   set d1 to today's short date string
   set d2 to short date string of (today + 6 * days)
   
   set dateRange to text returned of (display dialog "Enter the required date range:" default answer d1 & " - " & d2)
   set dateRangeStart to date (text from word 1 to word 3 of dateRange)
   set dateRangeEnd to date (text from word -3 to word -1 of dateRange)
   set dateRangeEnd's time to days - 1 -- Sets the last date's time to 23:59:59, the last second of the range.   
   return {dateRangeStart, dateRangeEnd}
end getDateRange

-- Return the start dates and summaries which are in the given date range.on filterToDateRange(theStartDates, theSummaries, dateRangeStart, dateRangeEnd)
   set {eventDatesInRange, eventSummariesInRange} to {{}, {}}
   repeat with i from 1 to (count theStartDates)
       set thisStartDate to item i of theStartDates
       if (not ((thisStartDate comes before dateRangeStart) or (thisStartDate comes after dateRangeEnd))) then
           set end of eventDatesInRange to thisStartDate
           set end of eventSummariesInRange to item i of theSummaries
       end if
   end repeat
   
   return {eventDatesInRange, eventSummariesInRange}
end filterToDateRange

-- Sort both the start-date and summary lists by start date.on sortByDate(eventDatesInRange, eventSummariesInRange)
   -- A sort-customisation object for sorting the summary list in parallel with the date list.   script custom
       property summaries : eventSummariesInRange
       
       on swap(i, j)
           tell item i of my summaries
               set item i of my summaries to item j of my summaries
               set item j of my summaries to it
           end tell
       end swap
   end script
   
   CustomBubbleSort(eventDatesInRange, 1, -1, {slave:custom})
end sortByDate

-- CustomBubbleSort from "A Dose of Sorts" by Nigel Garvey.-- The number of items to be sorted here is likely to be small.on CustomBubbleSort(theList, l, r, customiser)
   script o
       property comparer : me
       property slave : me
       property lst : theList
       
       on bsrt(l, r)
           set l2 to l + 1
           repeat with j from r to l2 by -1
               set a to item l of o's lst
               repeat with i from l2 to j
                   set b to item i of o's lst
                   if (comparer's isGreater(a, b)) then
                       set item (i - 1) of o's lst to b
                       set item i of o's lst to a
                       slave's swap(i - 1, i)
                   else
                       set a to b
                   end if
               end repeat
           end repeat
       end bsrt
       
       -- Default comparison and slave handlers for an ordinary sort.       on isGreater(a, b)
           (a > b)
       end isGreater
       
       on swap(a, b)
       end swap
   end script
   
   -- Process the input parameters.   set listLen to (count theList)
   if (listLen > 1) then
       -- Negative and/or transposed range indices.       if (l < 0) then set l to listLen + l + 1
       if (r < 0) then set r to listLen + r + 1
       if (l > r) then set {l, r} to {r, l}
       
       -- Supplied or default customisation scripts.       if (customiser's class is record) then set {comparer:o's comparer, slave:o's slave} to (customiser & {comparer:o, slave:o})
       
       -- Do the sort.       o's bsrt(l, r)
   end if
   
   return -- nothing end CustomBubbleSort

-- Compose the text from the items in the start-date and summary lists.on composeText(eventDatesInRange, eventSummariesInRange)
   set txt to ""
   set gap to linefeed & linefeed
   
   repeat with i from 1 to (count eventDatesInRange)
       set txt to txt & (date string of item i of eventDatesInRange) & (linefeed & item i of eventSummariesInRange & gap)
   end repeat
   
   return text 1 thru -3 of txt
end composeText

on main()
   tell application "iCal" to set {theStartDates, theSummaries} to {start date, summary} of events of calendar "FL Family Calendar"
   
   set {dateRangeStart, dateRangeEnd} to getDateRange()
   set {eventDatesInRange, eventSummariesInRange} to filterToDateRange(theStartDates, theSummaries, dateRangeStart, dateRangeEnd)
   sortByDate(eventDatesInRange, eventSummariesInRange)
   set txt to composeText(eventDatesInRange, eventSummariesInRange)
   
   tell application "TextEdit"
       make new document with properties {text:txt}
       activate
   end tell
end main

main()

Update 8/6/13: Clark Goble codes up comingevents.py, which uses PyObjC, AppScript, Osax and, of course, Python. It includes Parsedatetime so date entry can be free text and outputs plain text, HTML, Clipboard and probably sends a message to another dimension. I think at this point we've got scripted plaintext event publication covered. Be sure not to miss his links to related posts.