Telling Hazel not to match locked files

Hazel is a centrepiece of my automation suite on macOS. I rely on it to watch directories and take complex actions on files contained within them. Recently I discovered an issue with files that are locked in the Finder. If files that otherwise match all the rules are locked, then Hazel will attempt to execute the rules. But the locked status may preclude execution. For example, I began seeing frequent Hazel notifications popups such as:

Unable to execute the Move action on the matched file, Hazel gives up with a Notification. Instead I would prefer that it not match the file in the first place. But how to do that? There’s no criterion for locked/unlocked file status. Even extended criteria in the “Other…” list seems to have no option for this.

The solution is to use a Passes shell script condition in our rule, using the following script:

ls -lO "$1" | grep -q "uchg" && exit 1 || exit 0

Explanation

  1. The file to be tested is passed to the script as $1 and we are expected to exit with 0 if the file matches and non-zero if the file does not match.
  2. The ls command on Unix system lists directory contents. If we pass a file to ls then is returns only the information about that file. So ls "$1" would just provide basic information about the file. To find any flags on macOS we need two options. The first option -l gives us access to the long format and the -O flag provides file flags. We compress these options into ls -lO.
  3. The flag we’re looking for is uchg so we pipe the ls results to grep which tries to match the uchg flag. We use the -q because we don’t care about the contents of the matching line; we just want to know whether it matches or not.
  4. Finally we exit with 1 (failed match) if uchg is present and 0 if absent. This way files that are locked do not match.

Extra

If you have the macOS developer command line tools installed, you will have getfileinfo installed and you could also use that to find the locked status of a file. For example running getfileinfo -aL "my_unlocked_file" will return 0. Whereas running it on a locked file will return 1.

Possibly there’s a simpler way to accomplish this; but for now this is what I’m going with. Enjoy.

References

Quickly change playlist view options on macOS

While Apple is slowly coming around to recognizing that some of its users listen to classical music, there is one quirk in the Music app on macOS that betrays its deep bias toward pop music. It’s this: when you create a new playlist, the application defaults to displaying the tracks in its “Playlist” view, which as far as I can tell serves no other function than to consume real-estate in the UI by displaying a thumbnail of the album art.

This is completely wasted space. The album art adds nothing. I just want a simple list of tracks. And please include the composer. Since the Music application refuses to allow me to set that as a permanent preference, we’re stuck with automating the reversion to our preferred view format.

Enter Keyboard Maestro. Again, it comes to the rescue. The order of actions is this:

  1. ⌘J to bring up the view options dialogue.
  2. Select “Songs” in “View As:”
  3. Select “Albums” in “Sort By:”
  4. Select “Composer” checked.
  5. Close the view options dialogue.

I’ve programmed this macro to respond to ⌃P (for “playlist”.)

Now, the Music app will display my playlist as just a simple list of tracks, including the composer.

Obsidian file creation date debacle and a solution

Obsidian is pretty reckless with file creation dates. If you modify a note in Obsidian, it updates the file creation date. This renders Dataview queries that rely on it useless. For an introduction to this issue, see this lengthy thread on the Obsidian forums.

Workarounds

There are a several solutions to this problem.

1. YAML-based dates

One can include a cdate (or similar) field in the note’s front matter and just direct the Dataview query against that, e.g. LIST FROM "" WHERE startswith(cdate,"2023-05-29") SORT file.ctime asc. This works, but of course it requires you to always place that field ahead of your note content. Some people like that; others not so much.

Changing the file creation date on macOS

If you modify a file in-place using sed with the -i option, you will get a file that has a new file creation date. On macOS 13.3.1, this is absolutely 100% true, although you will read claims otherwise. I ran into this problem while implementing a Hazel rule that updates YAML automatically in my Obsidian notes.

Background

I have use YAML frontmatter in my Obsidian notes. It looks like:

---
uid:     20221120152124
aliases: [20221120152124, AllAboutShell]
cdate:   2022-11-20 15:21
mdate:   2023-05-18 05:14
type:    zettel
---

My goal is to update the mdate field whenever the file changes. Hazel is the perfect tool for this, so I set about writing a rule that covers this case. The heart of the rule is a shell script action that writes the modification date:

Flatten airports in X-Plane

Some airports in X-Plane have terrain issues that can be quite entertaining.

This Delta 737-800 got lost in the maze of cargo ramps at PANC and was trying to taxi back to the terminal when it encountered a steep icy taxiway. It required 65% N1 just to get up the slope.

Clearly a fix is required. It turns out to be quite simple. In the global airports file apt.dat, find the offending airport. In this case, it’s PANC where its entry looks like:

Hazel deletes custom file icons, and a workaround

I use Hazel extensively for automating file management tasks on my macOS systems. Recently I found that Hazel aggressively matches an invisible system file that appears whenever you use a custom file or folder icon. I’ll describe the problem and present a workaround.

In a handful of directories, I have a rule that prevents users (me) from adding certain file types. So the rule just matches any file that is not an image, for example, and deletes it. This is all well and good until to try to add a custom icon to this directory. Since the file Icon? that gets created as a result is not an image, the Hazel rule dutifully deletes it.

AwesomeTTS Anki add-on: Use Amazon Polly

As its name implies, the AwesomeTTS Anki add-on is awesome. It’s nearly indispensable for language learners.

You can use it in one of two ways:

  1. Subscribe on your own to the text-to-speech services that you plan to use and add those credentials to AwesomeTTS. (à la carte)
  2. Subscribe to the AwesomeTTS+ service and gain access to these services. (prix fixe)

Because I had already subscribed to Google and Azure TTS before AwesomeTTS+ came on the scene, there was no reason for me to pay for the comprehensive prix fixe option. Furthermore, since I’ve never gone above the free tier on any of these services, it makes no sense for me to pay for something I’m already getting for free. For others, the convience of a one-stop-shopping experience probably makes the AwesomeTTS+ service worthwhile.

Using fswatch to dynamically update Obsidian documents

Although I’m a relative newcomer to Obsidian, I like what I see, especially the templating and data access functionality - both that provided natively and through the Templater and Dataview plugins.

One missing piece is the ability to dynamically update the YAML-formatted metadata in the frontmatter of Obsidian’s Markdown documents. Several threads on both the official support forums and on r/ObsidianMD have addressed this; and there seems to be no real solution.1 None proposed solution - mainly view Dataview inline queries or Templater dynamic commands - seems to work consistently.

Week functions in Dataview plugin for Obsidian

There are a couple features of the Dataview plugin for Obsidian that aren’t documented and are potentially useful.

For the start of the week, use date(sow) and for the end of the week date(eow). Since there’s no documentation as of yet, I’ll venture a guess that they are locale-dependendent. For me (in Canada), sow is Monday. Since I do my weekly notes on Saturday, I have to subtract a couple days to point to them.

Scraping Forvo pronunciations

Most language learners are familiar with Forvo, a site that allows users to download and contribute pronunciations for words and phrases. For my Russian studies, I make daily use of the site. In fact, to facilitate my Anki card-making workflow, I am a paid user of the Forvo API. But that’s where the trouble started.

When the Forvo API works, it works OK, often extremely slow. But lately, it has been down more than up. In an effort to patch my workflow and continue to download Russian word pronunciations, I wrote this little scraper. I’d prefer to use the API, but experience has shown now that the API is slow and unreliable. I’ll keep paying for the API access, because I support what the company does. And as often as not when a company offers a free service, it’s likely to be involved in surveillance capitalism. So I’d rather companies offer a reliable product at a reasonable price.