Process Files Displayed in Smart Folder (Saved Search)

Is it possible to process all items in a Smart Folder? As an example, here's a Smart Folder that displays only items (in this case, folders) with a certain Finder tag. The tagged items might be from anywhere on any of my drives.

I'd like to copy any .m4a files found inside each of these folders to a dedicated Dropbox folder.

MacSparky.com has a chapter in his Hazel field guide titled 'Monitoring Smart Folders'

If you have Hazel, this could lead to something.

Thanks for the heads up but I don't have access to the Field Guide. Here's where I'm up to with my own experiments...

This Hazel rule watches the Smart Folder for any new tagged files/folders:

This triggers a KM macro that recursively checks for any .m4a files and copies them to the Dropbox folder if found.
TEST.kmmacros (22 KB)

Macro screenshot

If it is possible to delete files from the Dropbox folder when their parent directories become untagged, it won't be simple, so I might quit while I'm ahead!

That part does sound tricky. (and above my present skills :sunglasses:)
Seems you would have to pause the Dropbox syncing or be going 'round and 'round.

tell application "Finder"
   # tell front Finder window -- if you want to use the front window.
   tell Finder window "Pending"
      set smartFolderContents to its items
   end tell
end tell
  • Tested on macOS 10.14.6

I'd think you could do that directly with Hazel...

You'd have to periodically scan the files in the Dropbox directory and test the tagged-status of their parent directories.

Keyboard Maestro would be slow if you have a lot of files to scan, so I would probably use the shell or AppleScript or both.

This task might be a bit tedious, but it should be quite doable.jb

1 Like

Thankyou Chris! That AppleScript will no doubt be very handy for other things! However, what I like about Hazel is that it can actually monitor a Smart Folder as though it were a regular folder, and can therefore trigger a macro whenever anything is added to it by being tagged. I can't find a way to do that with KM.

This got my brain going and I came up with something. At the end of the macro, it adds the tagged file/folder path as a comment in the copied file attributes.

Copy .m4a Files in "NEXT" to Dropbox.kmmacros (44 KB)

Macro screenshot

Whenever I want to tidy up the Dropbox folder (by trashing any files whose originals have been untagged), I run a second macro which references the comments in all the copied .m4a files.

Remove Untagged "NEXT" Files From Dropbox (SUB-).kmmacros (40 KB)

Macro screenshot

It would be nice if this was triggered whenever a file was removed from the Smart Folder (i,e, untagged), but I can't figure out how to do that. Still, this is pretty nice! It means all I have to do is maintain my tags and occasionally do a tidy-up. Getting the tagged files into the Dropbox folder is more important to me than keeping extraneous ones out.

Right. KM cannot automatically monitor a smart folder – you'd have to poll it with a periodic trigger, store the result, poll it again, and do a diff to see if it had changed.

Doesn't Hazel have the ability to detect whether a file was removed? If not – it would seem to be a very strange limitation...

I'm sure it does. I just don't know the software well enough. I'll RTFM in a bit. Thanks again Chris.

1 Like

Hmm. Had a good read of the manual and scoured the Noodlesoft forum but could only find conditions for whether the number of files was above or below a certain number or whether that number had changed. Nothing on whether the number had increased or reduced.

Oh well, what I have already will do, I think.

Argh! I've always felt Hazel was too awkward to bother with, despite it's obvious good qualities...

At least it's no longer just a preference pane.

After a bit of back and forth, I got a definitive answer on the Noodlesoft forum. Hazel can tell if a file count has changed, but not whether it has increased or decreased. As you say, an oversight.

I'm confused. It looks from your screenshot above as if Hazel can return a file path to a newly-tagged file/folder after it appears in the Smart Folder (theFile that's then used as the parameter).

Presumably when something is removed theFile is either the path to what's removed, the path to the parent, empty, or something else equally obviously different to the "file/folder added" value? That, in conjunction with a file count change, should be enough to determine something was removed.

Whether you can do anything about it is another matter. But, at its heart, a Smart Folder is a constantly updating Spotlight search -- maybe you could use mdls with the appropriate arguments for that Smart Folder and compare that to a Dropbox file listing. Or, as Chris suggested earlier, keep a running list of Smart Folder contents and diff the current state against the previous one.

As it turns out, Hazel doesn't react to files being removed from a Smart Folder.

I just tried mdls and couldn't see a matching result count... but that could be me not really knowing what I'm looking at.

Terminal Readout
_kMDItemDisplayNameWithExtensions  = "- NEXT.savedSearch"
kMDItemAlternateNames              = (
    "- NEXT.savedSearch"
)
kMDItemContentCreationDate         = 2022-10-23 13:55:12 +0000
kMDItemContentCreationDate_Ranking = 2022-10-23 00:00:00 +0000
kMDItemContentModificationDate     = 2023-03-18 09:46:07 +0000
kMDItemContentType                 = "com.apple.finder.smart-folder"
kMDItemContentTypeTree             = (
    "com.apple.finder.smart-folder",
    "public.data",
    "public.item"
)
kMDItemDateAdded                   = 2022-10-23 13:55:12 +0000
kMDItemDisplayName                 = "- NEXT"
kMDItemDocumentIdentifier          = 0
kMDItemFSContentChangeDate         = 2023-03-18 09:46:07 +0000
kMDItemFSCreationDate              = 2022-10-23 13:55:12 +0000
kMDItemFSCreatorCode               = ""
kMDItemFSFinderFlags               = 16
kMDItemFSHasCustomIcon             = (null)
kMDItemFSInvisible                 = 0
kMDItemFSIsExtensionHidden         = 1
kMDItemFSIsStationery              = (null)
kMDItemFSLabel                     = 0
kMDItemFSName                      = "- NEXT.savedSearch"
kMDItemFSNodeCount                 = (null)
kMDItemFSOwnerGroupID              = 20
kMDItemFSOwnerUserID               = 501
kMDItemFSSize                      = 8332
kMDItemFSTypeCode                  = ""
kMDItemInterestingDate_Ranking     = 2023-03-29 00:00:00 +0000
kMDItemKind                        = "Saved Search Query"
kMDItemLastUsedDate                = 2023-03-29 21:34:30 +0000
kMDItemLastUsedDate_Ranking        = 2023-03-29 00:00:00 +0000
kMDItemLogicalSize                 = 8332
kMDItemPhysicalSize                = 12288
kMDItemUseCount                    = 161
kMDItemUsedDates                   = (
    "2022-10-22 23:00:00 +0000",
    "2022-10-23 23:00:00 +0000",
    "2022-10-24 23:00:00 +0000",
    "2022-10-25 23:00:00 +0000",
    "2022-10-28 23:00:00 +0000",
    "2022-10-29 23:00:00 +0000",
    "2022-10-31 00:00:00 +0000",
    "2022-11-05 00:00:00 +0000",
    "2022-11-14 00:00:00 +0000",
    "2022-11-17 00:00:00 +0000",
    "2022-12-14 00:00:00 +0000",
    "2023-01-15 00:00:00 +0000",
    "2023-01-22 00:00:00 +0000",
    "2023-01-24 00:00:00 +0000",
    "2023-02-10 00:00:00 +0000",
    "2023-02-11 00:00:00 +0000",
    "2023-02-12 00:00:00 +0000",
    "2023-02-13 00:00:00 +0000",
    "2023-02-21 00:00:00 +0000",
    "2023-02-23 00:00:00 +0000",
    "2023-02-25 00:00:00 +0000",
    "2023-02-28 00:00:00 +0000",
    "2023-03-04 00:00:00 +0000",
    "2023-03-05 00:00:00 +0000",
    "2023-03-18 00:00:00 +0000",
    "2023-03-20 00:00:00 +0000",
    "2023-03-21 00:00:00 +0000",
    "2023-03-23 00:00:00 +0000",
    "2023-03-26 00:00:00 +0000",
    "2023-03-28 23:00:00 +0000"
)

Hey Neil,

It's a good idea to show what you ran from the command line, so others can test verbatim

From what I see you ran:

mdls <path to smart search file>

And that's returning the metadata for the smart-folder file alone.

MDLS(1) BSD General Commands Manual MDLS(1)

NAME
mdls -- lists the metadata attributes for the specified file

mdls has no facility for analyzing smart-folders.

-Chris

Sorry Chris, my bad. I intentionally left that out so as not to post details of the project name associated with the Smart Folder on here... but yes, that's right.

Thanks for confirming. It sounds like perhaps there are a few strategies for dealing with Smart Folders, but for now I'll stick with the manual cleanup. No biggie. Thanks all.

1 Like

Just change the name(s) to protect the innocent...

:sunglasses:

I think you're old enough to get the reference, but being a brit may nix that (unless you watched American TV as a kid or reruns later on).

1 Like

G'ah! Apologies for the brain-fart... You'd have thought I'd have realised while typing it that mdls is to list (ls) metadata (md). The correct command, of course, is mdfind.

But that sent me back to the man page for mdfind and, lo and behold:

    -s <name>         Show contents of smart folder <name>

So if you've a Smart Folder called "Desktop PDFs" you can list the contents with

mdfind -s "Desktop PDFs"

So I'm thinking that, since you're you and are probably untagging with a macro, you could do something like

save output of mdfind to variable "A"
untag files/folders
save output of mdfind to variable "B"
for each line in "A"
   if not in "B" remove from Dropbox
end if
noisyneil@Neils-MacBook-Pro ~ % mdfind -s "NEXT"
noisyneil@Neils-MacBook-Pro ~ % 

Nothing comes back, which seems odd...

I'm not sure it would solve my particular problem. The sticking point isn't how to compare and delete; it's how to trigger it automatically when a tag is removed.

The manual method I'm currently using does the following:

  • Check the comment attribute for each file in the Dropbox Folder. The comment was set to the original file's path when copied over initially.
  • Use the comment path to check the original file for the tag NEXT.
  • If the tag is not found, delete the corresponding file in the Dropbox folder.

The reason I ended up with this is that I'm not copying the tagged folders in the Saved Search; I'm copying any mp3 or m4a files found within them.

I did just think of something though. Why not run the Dropbox cleanup at the start of the copy macro? That way, whenever I tag a file it deletes any copies of files from untagged folders and then copies files from tagged folders. It's not perfect, but it does mean I don't need to manually intervene.

Dropbox - Copy Tagged .m4a Files to "NEXT" (Hazel Trigger).kmmacros (50 KB)

Macro screenshot

I was assuming you were removing tags with a macro as well. If not then yes, you could do it either do it as part of the copy macro or run it on a periodic trigger. The latter would be where mdfind would be good, as that would be relatively quick and a low cost way of comparing "now" to "5 minutes ago".

But if it ain't working...

1 Like

Interesting. The -s switch isn't available on Mojave.

I'm happy though that Apple is actually improving something here and there...