I made a PopClip extension for Choosy. Download it here. View the source on GitHub.
Published on by
Published on by
Published on by
I was looking around in my home directory and found a file from 2013. That was the year that I finished my MBA, and I was applying for new jobs — and judging by the content of the file, I was thoroughly frustrated with many companies’ job application websites. Here’s the list of annoyances I catalogued in that file.
Unhelpful error messages:
Asking you to provide superfluous information:
Not understanding that answering a question a certain way precludes you from answering a follow-up question:
Asking you to provide the same information twice:
And finally, this oddly worded question:
I’m not sure why they wrote such a long, casually worded sentence when they could’ve just written “salary requirements.” It sounds like someone was dictating the question, and it was copied into the application verbatim.
Backing up Music playlists in macOS Catalina
Published on by
After one of my Music playlists disappeared from my Mac and my iPhone, I wanted a way to make automatic, scheduled backups of my playlists. On macOS Catalina, this turned out to be harder than I expected.
Catalina’s Music app allows you export a playlist as an XML file. If the playlist is ever deleted, you can import that XML file to recreate the playlist. But this is a manual, cumbersome process. You have to manually select each playlist, click File > Library > Export Playlist and — as long as you’re OK with the default file name and folder — click Save.
I thought I could automate this process with AppleScript, but the Music app’s AppleScript interface doesn’t provide an export capability. The logical fallback was UI (user interface) scripting via System Events, which allows you to script UI actions like clicking a menu. This came with its own challenges.
First, you have to know the correct names of the UI elements. Almost nothing has an intuitive name that you could grasp by visually inspecting the application window. First, I used this Keyboard Maestro macro to get a list of all of the UI elements. It outputs a plain text file, so I just searched that text file for the name of a playlist.
A portion of the output of the Keyboard Maestro macro:
application Process "Music"
window Music
splitter group 1
scroll area 1
outline 1
...
row 23
UI element 1
static text "80s"
Once I knew where the playlists were in the UI hierarchy, I used Script Debugger’s Explorer, which allows you to drill down from System Events to the relevant process (application) and from there to the application’s UI elements. Once you find the element you need, you can right-click it and select Copy Reference
to copy an AppleScript code block that will provide access to that element. (At $99.99, Script Debugger is expensive, but there’s a free trial, and it offers many powerful features beyond the Explorer.)
(There are other ways to get this information, including Xcode’s Accessibility Inspector, which is free but not very intuitive.)
There’s still some trial and error involved to distinguish the UI elements that can perform or accept actions from the elements that are just containers to navigate through. For example, to get the name of a playlist with System Events, you have to drill through System Events > process “Music” > window “Music” > splitter group 1 > scroll area 1 > outline 1 > row > UI element 1 > static text. The static text element contains a name
property that contains the name of the playlist. (It also contains a value
property that contains the same value. I don’t know if one or the other is the “correct” property in this case, but name
worked for me.)
If you want to interact with the playlist, however, you refer to the row. But do you click
it or select
it? According to its definition, it responds to both. Finding the right one, select
, was just trial and error.
After figuring out all of that, and doing a little more work, I had a working script that could export all of my playlists without any action on my part (other than running the script). Here’s the completed script (and GitHub repo if you’d rather see it there):
use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions
on pathExists(pathName)
(* Check if pathName is an existing file or folder.
First, try to get a reference pathName by using alias. If it succeeds, return true.
If it fails, try to get a reference to pathName as a POSIX path. If it succeeds, return true.
Else return false.
*)
try
pathName as alias
return true
on error
try
POSIX file pathName as alias
return true
on error
return false
end try
end try
end pathExists
set alertResult to display alert ¬
"Back up Music playlists now?" message "Quitting in 10 seconds..." buttons {"Yes", "No"} ¬
default button "Yes" giving up after 10
set runBackup to button returned of alertResult
set gaveUp to gave up of alertResult
if runBackup = "No" or gaveUp then
error number -128
end if
set backupFolder to "Playlist Backup" -- The name of the folder that will contain the backups, grouped in subfolders by date
set baseFolder to POSIX path of (path to documents folder) & backupFolder -- backupFolder is a subfolder of ~/Documents
set baseFolderPath to POSIX file baseFolder -- Get baseFolder's POSIX path so that we can concatenate it with the current date
set currentDate to do shell script "date +'%Y%m%d'" -- Get the current date in YYYYMMDD format
set saveFolder to POSIX path of baseFolderPath & "/" & currentDate -- The path to today's folder
set baseFolderExists to pathExists(baseFolderPath) -- Check if baseFolder exists
set saveFolderExists to pathExists(saveFolder) -- Check if saveFolder exists
set the clipboard to saveFolder -- Put saveFolder's path on the clipboard so we can use it later
tell application "Finder"
if baseFolderExists then
-- If baseFolder exists, check if saveFolder exists. If not, create saveFolder.
if saveFolderExists is false then set newSaveFolder to make new folder at baseFolderPath with properties {name:currentDate}
else
-- Else create baseFolder and saveFolder
set newBaseFolder to make new folder at (path to documents folder) with properties {name:backupFolder}
set newSaveFolder to make new folder at baseFolderPath with properties {name:currentDate}
end if
end tell
tell application "Music"
activate
set userPlaylists to (name of every user playlist whose name does not start with "Purchased" and smart is false) -- Get all user playlists except "Purchased on iPhone," etc. and smart playlists
end tell
(* The Music app's AppleScipt interface doesn't offer a way to export playlists, so we have to script the UI using System Events.
This block iterates over the playlists in the Music app. When it finds a playlist with a name that matches userPlaylists,
it selects the playlist, clicks File > Library > Export Playlist, and saves the playlist as an XML file in saveFolder.
This uses the default file name, which is <playlist name>.xml.
*)
tell application "System Events"
tell its process "Music"
tell its window "Music"
tell its splitter group 1
tell its scroll area 1
tell its outline 1
repeat with thisRow in (every row)
tell thisRow
tell its UI element 1
repeat with staticText in (every static text)
if userPlaylists contains (name of staticText) then
set selectedRow to select thisRow
tell application "System Events"
tell its process "Music"
set focused of window "Music" to true
delay 2
click menu item "Export Playlist…" of menu "Library" of menu item "Library" of menu "File" of menu bar 1
delay 2
tell its window "Save"
keystroke "G" using {command down, shift down} -- Shift-Cmd-G to change directories using the "Go to Folder" dialog
delay 2
keystroke (the clipboard) -- Paste saveFolder's location into the "Go to Folder" dialog
delay 2
keystroke return
delay 2
click button "Save"
delay 1
end tell
end tell
end tell
end if
end repeat
end tell
end tell
end repeat
end tell
end tell
end tell
end tell
end tell
end tell
tell application "Music"
quit
end tell
My next challenge was getting the script to run on a schedule. Since Keyboard Maestro can run AppleScripts, I first tried creating a macro with an “At time” trigger. That worked fine up until the tell application "System Events"
block, which didn’t run. After doing some research, I concluded that a launch agent was probably the right approach. After more research, I concluded that manually creating the file wouldn’t be fun, so I bought LaunchControl. It made the task of creating the launch agent easy. I tested the launch agent, and everything worked. But I wanted it to run at night when I’m asleep, not while I’m using my computer. I ran another test when the display, but not the computer, was sleeping. This worked until it hit the tell application "System Events"
block, then it stopped. Eventually I figured out that, while the script will run when the display is asleep, the Music app doesn’t get focus, and System Events can’t click the menus if Music isn’t the frontmost app. I found and tried several possible solutions, but nothing worked. I concluded that the script will only work if the display is awake.
The solution I settled on isn’t ideal, but at least now I can back up my playlists with almost no action on my part. I added a display alert
line at the beginning of the script which asks if I want to run the backup job. If I click “No” or do nothing for 10 seconds, the script just quits. If I click “Yes,” the script runs, and I have to stop using the computer for several minutes so that the Music app stays in the foreground.
I changed the launch agent schedule to run at a time in the late afternoon when I’m likely to be using the computer. I no longer have to think about backing up my playlists; I just have to be in front of the computer at the right time so that I can click “Yes.”
It would be nice if Apple would update the scripting interface so that this could be done without UI scripting and thus happen completely in the background (or revert to storing playlists as XML files in the file system so that I could just include them in my regular backups). At any rate, I have a solution that requires almost no effort on my part, and that’s good enough for now.
Switching to Thunderbird from Apple Mail
Published on by
Updated on
A long time ago, and for many years, I used Thunderbird as my desktop email client. I don’t remember when I stopped using it. I know I wasn’t using it when I bought my MacBook Air in 2011, so it was roughly nine years ago, if not longer. My wife kept using Thunderbird, and every time I saw it, I wasn’t even sure it was under active development; it never seemed to change.
I tried newer email clients over the years, but settled on Apple Mail because it was simple and met all of my needs:
At some point during the years that I used Mail, I started using rules to flag financial emails (bills, bank statements and receipts). I got by with colors and flags, which are the only options available natively. In January, I decided I really didn’t like the color/flag system anymore. I really wanted tags, but Mail has no native tag feature. I searched for a solution and found a capable but expensive add-on called MailSuite. I liked the tagging capability added by MailSuite, but it costs $60, which I couldn’t justify spending just for tags. (It has other features, but I wasn’t using them.) There’s also the danger that, since it’s an add-on to a native Apple app, it could be rendered useless by future changes to Apple’s APIs.
Around the same time, I read this post on the Thunderbird blog and realized that Thunderbird was alive and well. It has native tags, so I decided to give it another try. I alternated between using Thunderbird and Apple Mail for a while, but before long I was using Thunderbird exclusively.
I found that Thunderbird satisfies the four main requirements I listed above, and it has several other features that made it the clear winner.
Tags are the main feature that brought me back to Thunderbird. If I need to save an email for future reference (an airline reservation, for example), I’ll file it in a folder. But many emails don’t need to be saved permanently; they need attention temporarily, but not always immediately. I use tags and rules to categorize and triage those emails, and it’s much easier to read a tag than it is to remember what a color means.
Tags are created under Preferences > Display > Tags.
The one thing I don’t like about Thunderbird’s tags is that you can’t change the order of the tags, and the order determines the shortcut key for each tag. (There’s an 11-year-old bug for this very issue.)
Tags can be assigned to messages in a number of ways. You can right-click a message and select the tag from the Tag submenu.
With the message selected, you can click the Tag button in the Mail Toolbar, or you can just press the keyboard shortcut for the tag. The shortcut for each tag is shown in the menu. (The shortcut key is determined by the tag’s position in the tag list.)
In addition, message filters can automatically assign tags based on conditions that you define.
You can also assign a color to each tag. Messages assigned a particular tag are displayed in that color. If you have the Tag column displayed in the message list pane, the message’s tags are displayed there.
If the Tag column isn’t displayed, right-click any column heading in the message list pane and select Tag from the menu.
Tags can be used in Saved Searches (Thunderbird’s analogue to Apple Mail’s Smart Mailboxes). I use this feature to show all of my financial emails (and only those emails) in one folder.
Thunderbird provides a couple of useful privacy and security features by default. First, remote content in messages is blocked by default. Images and other content loaded from a remote server are a common way to track if a specific person has opened an email, as well as other information you may not want to share. (This isn’t limited to just the images you see; some emails contain very tiny images called tracking pixels.) Since Thunderbird blocks remote content by default, you’re protected from this tracking mechanism.
If you click the Preferences menu in any message where remote content was blocked, you’ll see the origin of all remote content in the message, as shown in the image below. From the menu, you can display the remote content in the message (Show remote content in this message), allow remote content from one or more of the originating domains (e.g., Allow remote content from https://cms.qz.com) or allow remote content from all of the sender’s messages (e.g., Allow remote content from [email protected]).
Although I wouldn’t recommend it, you can also allow all remote content by enabling the Allow remote content in messages setting under Preferences > Privacy.
I really like how this setting is presented. Both its wording and the fact that it’s disabled by default make it clear that this is an explicit opt-in setting. Even better, there’s a link that provides more detail about this setting and why it’s important to your privacy.
Apple Mail has the same feature, but I like Thunderbird’s functionality and presentation better. In Mail, it’s under the Viewing preferences. While that makes sense, I think framing it as a privacy setting is a better approach. Thunderbird also allows more detailed control over remote content. For example, Apple Mail loads either all or none of the remote content. You can’t allow it per origin or per sender as you can in Thunderbird.
The second feature is one that I haven’t seen in any other email client. If you click a link in an email, and the link text doesn’t match the actual link location, Thunderbird displays a warning and asks you which link you actually want to follow.
There’s one other feature that I really like: I can prevent messages from being marked as read unless I specifically mark them as read. Sometimes I want to preview a message quickly and then come back to it later; in that case, I don’t want it marked as read. But Apple Mail marks a message as read immediately, even if you view it in the preview pane, and there’s no way to disable it. Thunderbird has an Automatically mark messages as read setting under Preferences > Display > Advanced. The setting can be disabled so that messages are never marked as read unless you mark them as read.
It wasn’t a big change to switch to Thunderbird from Apple Mail; both are standard desktop email clients with the same basic features. However, there are a few things I miss about Apple Mail. Fortunately I was able to find workarounds for most of them. The workarounds aren’t necessarily elegant, but they get the job done. These are minor inconveniences that I’m willing to put up with because Thunderbird has really useful features that Apple Mail lacks.
Apple Mail has the ⇧⌘R keyboard shortcut to mark a message as read or unread. Thunderbird doesn’t have a keyboard shortcut for this. While it would be possible to create a keyboard shortcut in macOS’s keyboard preferences, it would require two shortcuts: One for mark as read and one for mark as unread. (Update - July 18, 2020: Someone actually read this post and let me know that Thunderbird does have a keyboard shortcut to mark a message read or unread: M. And there are many other shortcuts that aren’t exposed in the menus.)
Instead, I used Keyboard Maestro so that I only have to remember one key combination (and it’s the same one I’ve been using for years in Apple Mail). If the As Read menu item is enabled, ⇧⌘R selects Thunderbird’s Message > Mark > As Read menu item. Otherwise it selects Message > Mark > As Unread.
I made a few other shortcuts just for convenience, rather than to replicate Mail’s functionality:
Message filters, which are called rules in Apple Mail, are created per-account in Thunderbird, whereas in Apple Mail all rules work in all accounts. Even though I only have two filters at the moment, I didn’t want to recreate and maintain them for each email account, especially since that list of rules may grow over time. (For several reasons — including a couple of merchants who, in the year 2020, can’t successfully process an email address change — I’m monitoring multiple accounts for financial transactions.) Fortunately, there’s an extension called quickFilters that enables copying filters from one account to another. It would be better if Thunderbird just had native global filters, but the extension gets the job done.
Thunderbird still looks a lot like it did 10 years ago; its user interface hasn’t seen a lot of updates, and it isn’t as polished as Apple Mail’s. This is especially obvious if you use macOS in dark mode. Thunderbird doesn’t adjust its interface to match the operating system’s appearance. A dark theme can be enabled under Preferences > Extensions & Themes > Themes. However, some parts of interface, such as the preferences screen and context menus, don’t conform to the dark theme. Hopefully, since Thunderbird is actively developed now, the interface will get a refresh in the future.
Minor inconveniences aside, I’ve been happily using Thunderbird for several months now, and I’m glad to see that it’s being actively developed and improved. It’s a solid, uncomplicated email client that does everything I need it to (with a few minor workarounds).
The way I wish all software subscriptions worked
Published on by
Due is a fantastic reminder app that I’ve been using for almost five years, according to the screenshot below. Due gets a lot of things right: It’s powerful, yet not overly complicated. The interface looks really nice. Automation is relatively easy thanks to its well-documented URL scheme. Add to that the most consumer-friendly subscription model I’ve seen to date.
I have mixed feelings about software subscriptions. Some are very valuable to me, and I pay for them without a second thought. Others I just can’t justify, no matter how much I like the app. When the developer of Due announced that it was moving to a subscription model, I wasn’t sure which category it would fall into. But then I read the blog post explaining how the subscription works. It’s actually something that I wish more apps would do. When you subscribe, you get access to all new features released during your one-year subscription period, in addition to all existing paid features that you haven’t already unlocked. If you choose to cancel the subscription, you get to keep all of the features that you paid for, and you still get free app updates (but not access to new features). And I think it’s reasonably priced at $4.99.
I really like what the developer has done with the app’s subscription screen. It tells you when you purchased Due, when your access to upgrades ended, which paid features you’ve unlocked, and which features you’ll unlock (forever) if you subscribe.
I decided to subscribe for the improved Siri Shortcuts implementation. Knowing that I won’t lose any functionality if I decide to cancel certainly made the decision easier.