Getting the URL of the File Most Recently Added to a Folder

Hi

I would like to get the complete URL for a file that was most recently added to a folder 'Documents'

whats the best way to do this? as i want to pass the URL into a KM Variable.....

1 Like

How would I get this script to sort by most recently added to the folder? when i use ‘added date’ i get an error.

tell application "Finder"
	set latestFile to item 1 of (sort (get files of (path to documents folder)) by creation date) as alias
	set fileName to latestFile's URL
end tell


tell application "Keyboard Maestro Engine"
	make variable with properties {name:"Attaching File URL", value:result}
end tell

You can iterate through a folder and find the most recently added file like this:

Keyboard Maestro Actions.kmactions (3.7 KB)

How about this:

###The Script:

set sourceFolder to (path to documents folder)

tell application "Finder"

	set fileList to (sort (get files of sourceFolder) by creation date)
	
	-- This raises an error if the folder doesn't contain any files
	-- Last File is the Most Recently Created --
	set theFile to (last item of fileList) as alias
	
	set thePath to POSIX path of theFile
	set oProp to (properties of theFile)
	set URLstr to URL of oProp
	
end tell

return URLstr

Hi Peter,

Thank you for this it works, but sometime we have 1,000s of files in a particular folder. This method is taking way to long. How can we shorten it?

Hi JMichealTX,

Thank you for your contribution, your method works but i am trying to get the most recently added to the folder. Not the most recently created. I cant seem to sort by ‘added date’

Keyboard Maestro is designed to be faster than you, not faster than the computer. It is only designed to run at about 1000 actions per second, so if you have thousands of files, then it is going to take many seconds.

If you want something faster, you will have to find a script solution.

Hey Ali,

You can't.

The Finder's scripting dictionary does NOT support date added (blame Apple).

Furthermore the sort function in the Finder script you posted is grossly slow.

The only way to do this with alacrity and without examining each file is to use AppleScriptObjC.

Run from a Execute an AppleScript action.

-Chris

------------------------------------------------------------
# Auth: Christopher Stone & Shane Stanley
# dCre: 2016/02/09 06:58
# dMod: 2016/02/09 06:58 
# Appl: ASObjC
# Task: Get the most recent file path from the designated directory.
# Tags: @Applescript, @Script, @ASObjC, @Get, @Most, @Recent, @Path
------------------------------------------------------------

use framework "Foundation"
use scripting additions

set thePath to POSIX path of (path to documents folder as text)
set sortedList to its filesIn:thePath sortedBy:(current application's NSURLAddedToDirectoryDateKey)

on filesIn:folderPOSIXPath sortedBy:sortKey
  -- specify keys for the values we want for each item in the folder:
  -- we want the path, whether it's a directory, whether it's a package, and the key to sort on
  set keysToRequest to {current application's NSURLPathKey, ¬
    current application's NSURLIsPackageKey, ¬
    current application's NSURLIsDirectoryKey, ¬
    sortKey}
  -- make an NSURL for the folder because that's what's needed
  set theFolderURL to current application's class "NSURL"'s fileURLWithPath:folderPOSIXPath
  -- get a ref to the file manager
  set theNSFileManager to current application's NSFileManager's defaultManager()
  -- get list of NSURLs of items in folder, getting the values for our keys at the same time, and skipping invisible items
  set listOfNSURLs to (theNSFileManager's contentsOfDirectoryAtURL:theFolderURL ¬
    includingPropertiesForKeys:keysToRequest ¬
    options:(current application's NSDirectoryEnumerationSkipsHiddenFiles) ¬
    |error|:(missing value))
  -- create an array to store just the key values in
  set valuesNSArray to current application's NSMutableArray's array()
  -- loop through retrieving key values, adding each set as a dictionary/record to the array/list
  repeat with oneNSURL in listOfNSURLs
    (valuesNSArray's addObject:(oneNSURL's resourceValuesForKeys:keysToRequest |error|:(missing value)))
  end repeat
  -- filter out all directories that aren't packages using a predicate
  set theNSPredicate to current application's NSPredicate's predicateWithFormat_("%K == NO OR %K == YES", current application's NSURLIsDirectoryKey, current application's NSURLIsPackageKey)
  set valuesNSArray to valuesNSArray's filteredArrayUsingPredicate:theNSPredicate
  -- make a sort descriptor that describes the key to sort on
  set theDescriptor to current application's NSSortDescriptor's sortDescriptorWithKey:(sortKey) ascending:true
  -- sort the array
  set theSortedNSArray to valuesNSArray's sortedArrayUsingDescriptors:{theDescriptor}
  # -- extract just the paths and convert to an AppleScript list
  # return (theSortedNSArray's valueForKey:(current application's NSURLPathKey)) as list
  -- or if you only want the most-recent, use this instead of the previous line:
  return (theSortedNSArray's lastObject()'s valueForKey:(current application's NSURLPathKey)) as text
end filesIn:sortedBy:

# You can sort on other criteria, including: NSURLAttributeModificationDateKey, NSURLContentAccessDateKey, NSURLCreationDateKey, NSURLLabelColorKey, NSURLLabelNumberKey, NSURLLocalizedLabelKey, NSURLLocalizedTypeDescriptionKey, NSURLNameKey, NSURLTypeIdentifierKey, NSURLFileSizeKey, NSURLFileAllocatedSizeKey, NSURLTotalFileAllocatedSizeKey, and NSURLTotalFileSizeKey.

------------------------------------------------------------
2 Likes

Sorry I missed that key requirement.

Chris (@ccstone) has given you a great solution.

I had another idea, untested, that I thought I'd share with you (and everyone):
####Use a Folder Action Script

When a file is added to the folder, the script will run, and you can:

  • Get the file path and URL
  • Set KM variables to the above
  • Execute a KM macro
  • and do anything else you'd like

The script can handle one or more files added to the folder at the same time.

This might be faster than sorting if you have a large number of files in the folder.

That’s a clever idea @JMichaelTX. And you can also do this with a Folder trigger and detect files added to the folder and store the path to them or trigger your macro that way.

1 Like

Peter, I'd like to do this, but the %TriggerValue% returns ONLY the file name without a path. So all of the Get File Attribute actions fail.

  1. How do I get the full path?
  2. How do I get the URL once I have the full path?
  • URL is not available in the Get File Attribute choices

Results of Macro:


I will probably change it so TriggerValue returns the full path, even though that will break people already using it, but you generally know the path, so in this case it would be:

~/Documents/%TriggerValue%

Then you have to change a file path into a file URL. You can start by expanding the ~ which the Filter Variable action can do. After that you need to convert it in to a file URL if you really need it as a URL (I'm not sure why you would want it as a URL). Generally that is just encoding it and adding file:// to the front.

Right, I knew how to do that, hard-coding the path.

I guess what I was hoping for is a way to read the path of the folder set in the Trigger of the macro. Possible?

Given the current limitations of KM, maybe in this case an AppleScript Folder Action script is a better solution.

The file URL is very useful to in apps like Evernote where I can create a hyperlink using the file URL that will open the file when clicked.

The OP requested the URL.

Hey Peter,

I think that's highly desirable.

(The breakage won't be too hard to fix.)

-Chris

1 Like

Hey JM,

Why specifically?

The folder action script will return aliases, and you can use the Finder to coerce those to file-URLs.

Then you'd have to trigger a Keyboard Maestro macro from the AppleScript.

And you'd have more than one script/macro to maintain.

Not to mention that Folder Actions suck until El Capitan (supposedly they've finally been fixed, but I have not yet verified this.)

-Chris

Because:

  1. KM does not return the full path of the file that triggered the add
  2. KM does not provide a direct means of getting the URL

I don't see any maintanance difference between KM calling an AppleScript, or an AppleScript calling a KM macro.

It's just another tool in your toolbox. Choose whichever works best for you.

Hey JM,

You've used Folder Actions? They are rather awkward.

While some folks have had quite good luck with them, I (and many others) have found them to be very error prone in the past. I have also found them to make the Finder more prone to crashing.

However this does NOT mean I won't use them if they're the right tool for the job now they've been made more reliable.


###applescript Release Notes: OSX 10.11 Changes
Folder Actions now reliably notices all added and removed items in an observed folder.


Point 1 is dead simple to work around in Keyboard Maestro.

Point 2 is a stickier wicket, as Keyboard Maestro's encoding schema doesn't produce exactly what the Finder would.

I'll probably write a plugin to get around this soon. In the meantime it can be done with AppleScriptObjectiveC, which is faster than using the Finder.

------------------------------------------------------------
use framework "Foundation"
tell application "Finder" to set fileList to selection as alias list
repeat with i in fileList
  set posixPath to POSIX path of i
  set contents of i to (current application's class "NSURL"'s fileURLWithPath:posixPath)'s absoluteString() as text
end repeat

fileList
------------------------------------------------------------

As far as I know the single biggest advantage Folder Actions has over Keyboard Maestro's Folder Trigger is that it harvests all items added (as a batch) at once, so they can be operated on as a batch. This would probably be meaningless in the downloads folder, but it could be very useful in a folder where files are dragged and dropped en mass.

KM runs once per file added.

-Chris

1 Like

Chris,

I've had nothing but good luck with AppleScript Folder Actions. I have a Evernote Import folder action that I use several times a day, and it has never failed or crashed.

Hey Folks,

I’ve added a boolean to this allowing folders to be included or excluded from the result.

-Chris

---------------------------------------------------------------------------------
# Auth: Christopher Stone { Heavy Lifting by Shane Stanley }
# dCre: 2016/02/09 06:58
# dMod: 2016/06/02 22:19
# Appl: ASObjC
# Task: Get the most recent item path from the designated directory 02.
#     : Allows for exclusion of folders.
# Tags: @Applescript, @Script, @ASObjC, @Get, @Most, @Recent, @Path
---------------------------------------------------------------------------------
use AppleScript version "2.4"
use framework "Foundation"
use scripting additions
---------------------------------------------------------------------------------

# POSIX Path of the folder you're interested in.
set thePath to POSIX path of (path to downloads folder as text)

# Returns the POSIX Path of the last added item.
set sortedList to its filesIn:thePath sortedBy:(current application's NSURLAddedToDirectoryDateKey) includeFolders:false

---------------------------------------------------------------------------------
--» HANDLERS
---------------------------------------------------------------------------------
on filesIn:folderPOSIXPath sortedBy:sortKey includeFolders:includeFoldersBool
  # Specify keys for the values we want for each item in the folder:
  # We want the path, whether it's a directory, whether it's a package, and the key to sort on
  set keysToRequest to {current application's NSURLPathKey, ¬
    current application's NSURLIsPackageKey, ¬
    current application's NSURLIsDirectoryKey, ¬
    sortKey}
  # Make an NSURL for the folder because that's what's needed
  set theFolderURL to current application's class "NSURL"'s fileURLWithPath:folderPOSIXPath
  # Get a ref to the file manager
  set theNSFileManager to current application's NSFileManager's defaultManager()
  # Get list of NSURLs of items in folder, getting the values for our keys at the same time, and skipping invisible items
  set listOfNSURLs to (theNSFileManager's contentsOfDirectoryAtURL:theFolderURL ¬
    includingPropertiesForKeys:keysToRequest ¬
    options:(current application's NSDirectoryEnumerationSkipsHiddenFiles) ¬
    |error|:(missing value))
  # Create an array to store just the key values in
  set valuesNSArray to current application's NSMutableArray's array()
  # Loop through retrieving key values, adding each set as a dictionary/record to the array/list
  repeat with oneNSURL in listOfNSURLs
    (valuesNSArray's addObject:(oneNSURL's resourceValuesForKeys:keysToRequest |error|:(missing value)))
  end repeat
  
  # Filter out all directories that aren't packages using a predicate
  if includeFoldersBool ≠ true then
    set theNSPredicate to current application's NSPredicate's predicateWithFormat_("%K == NO OR %K == YES", current application's NSURLIsDirectoryKey, current application's NSURLIsPackageKey)
    set valuesNSArray to valuesNSArray's filteredArrayUsingPredicate:theNSPredicate
  end if
  # Make a sort descriptor that describes the key to sort on
  set theDescriptor to current application's NSSortDescriptor's sortDescriptorWithKey:(sortKey) ascending:true
  # Sort the array
  set theSortedNSArray to valuesNSArray's sortedArrayUsingDescriptors:{theDescriptor}
  
  # Extract just the paths and convert to an AppleScript list
  # return (theSortedNSArray's valueForKey:(current application's NSURLPathKey)) as list
  
  # Or if you only want the most-recent, use this instead of the previous line:
  return (theSortedNSArray's lastObject()'s valueForKey:(current application's NSURLPathKey)) as text
  
end filesIn:sortedBy:includeFolders:

# You can sort on other criteria, including: NSURLAttributeModificationDateKey, NSURLContentAccessDateKey, NSURLCreationDateKey, NSURLLabelColorKey, NSURLLabelNumberKey, NSURLLocalizedLabelKey, NSURLLocalizedTypeDescriptionKey, NSURLNameKey, NSURLTypeIdentifierKey, NSURLFileSizeKey, NSURLFileAllocatedSizeKey, NSURLTotalFileAllocatedSizeKey, and NSURLTotalFileSizeKey.

---------------------------------------------------------------------------------
3 Likes

Hey guys, I may be naive here, but what about a shell script? Like this:

ls -t "/Users/Dan/Documents/Keyboard Maestro/Work" | head -1