Are true dynamic folders (NOT a “Smart Folder” SavedSearch) possible?
-
14-04-2021 - |
Question
I'm using an application that loads the contents of a folder of audio samples for my use. The samples I want to use however, are scattered amongst many folders on my machine, and I don't want to copy them all into a single folder first.
Aliases work just fine, and I was able to make symlinks of all the desired files, put them in a single folder, and load that folder. Excellent solution, except for one thing.
I'm going to be adding more files over time that meet the criteria I use for this application. I would love to be able to use a dynamic folder that would essentially be what "Smart Folders" are, except a real folder. A Mac "Smart Folder" is not actually a folder at all, but really just creates a "SavedSearch" file, which is unreadable except by the Finder so my application can't see into it.
Is it possible to create a true dynamic folder that will just update as the filesystem changes? Or maybe being realtime would be too much of a strain, so on a schedule? I could see maybe concocting something in Automator but... I dunno sounds a bit heavy. Love to know if there's a better way!
Solution
I have found a solution to create a dynamic "soft link" folder:
Preface:
- $HOME is the result of the environmental variable HOME here. When you log in as user example_user, $HOME is then the path to the home folder of example_user: /Users/example_user. If $HOME appears in the text below you have to replace it yourself with /Users/your_user_name/ (e.g. saving the launch agent). If $HOME appears in a code box below (e.g. in the shell script) it will automatically expanded to your home folder by the (bash) shell while executing the script.
mdimport -A|sed $'s/\t\t/;/g;s/(null)//g'|tr -d \'
shows a lot of metadata attributes to choose from as search criteria (e.g. kMDItemAudioBitRate or kMDItemDurationSeconds)mdls /path/to/file
lists all metadata attributes of a chosen file
Creating a dynamic link folder:
Create a shell script samples.sh in $HOME/bin/sh/ with the following content:
#!/bin/bash # Define folders # Source folder (including files in subfolders) MusicSamples=$HOME/Music/Samples # Destination folder DrumFoldr=$HOME/Desktop/Samples # Remove orphaned soft links in the destination folder find $DrumFoldr -type l -exec sh -c 'for x; do [ -e "$x" ] || rm "$x"; done' _ {} + # Add soft links in destination folder depending on some criteria (here: BitRate and UserTag) for File in $(mdfind -onlyin $MusicSamples 'kMDItemAudioBitRate >= "44000" && _kMDItemUserTags = "Sample"') do ln -s $File $DrumFoldr done # Remove soft links in the destination folder depending on some criteria (removing UserTag from original file) for File in $(mdfind -onlyin $MusicSamples '_kMDItemUserTags != "Sample"') do filename=$(basename "$File") rm $DrumFoldr/$filename done
In the shell script define your music folder containing the sample files (in my example that's a Samples folder in the Music folder) and the "DrumFoldr" containing the soft links later (in my example that's a Samples folder on my Desktop).
Now choose some Spotlight search criteria (in my example I have chosen the bit rate and a user defined tag "Sample" - you can define user tags by right-clicking a file > Tags... and adding a tag by just writing something. The user defined tag is then available for other files and folders.
The above shell script will add only files with a sample rate of 44000 or higher and (this is a logical and!) with the user tag "Sample" as soft link to the DrumFoldr folder. So the destination folder will not contain links to files without the Sample tag but with a sample rate of 96k or files with the Sample tag but without a kMDItemAudioBitRate (like txt files or pdfs). It also does some cleansing in the DrumFoldr folder.
BTW: the music length can be searched/determined by using the key kMDItemDurationSeconds:
kMDItemDurationSeconds <= "2"
.Create a file usr.samplesync.plist in $HOME/Library/LaunchAgents (that means:
/Users/<username>/Library/LaunchAgents/
) with the content:<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>usr.samplesync</string> <key>ProgramArguments</key> <array> <string>/bin/bash</string> <string>/Users/<username>/bin/sh/samples.sh</string> </array> <key>RunAtLoad</key> <true/> <key>StartInterval</key> <integer>10</integer> </dict> </plist>
Replace
<username>
by your short user name. The shell script will be executed every 10 seconds. You can increase the interval depending on your needs and environment to higher values (e.g. 60 = 1 minute or 600 = 10 minutes)Load the launch agent with:
launchctl load $HOME/Library/LaunchAgents/usr.samplesync.plist
If you want to save the shell script somewhere else you have to apply the path in the plist accordingly.
Caveats:
- the original sample files mustn't contain spaces in their paths or file names (I try to solve this restriction later)
- the original sample files mustn't be moved or the removing of the user tag won't delete the soft link
Things to improve:
- don't create a soft link if a proper one already exists