Folder action: unwanted re-triggering after changing file name

KniazidisR

Your script behaves this way :

1 - receive a file named “azerty qwerty.txt”
2 - rename it as “azerty qwerty_1.txt”
after that it :
3 - warn the system that the folder has ‘received’ the file renamed as “azerty qwerty_1.txt”
4 - will not rename it.

So, it gives the final result but it doesn’t avoid “unwanted re-triggering after changing file name” which is the original question. It just avoid an unwanted renaming :rolleyes:

Have a good night. On my side it’s time to sleep.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) samedi 21 décembre 2019 22:15:58

The 4 steps is named idea. The script doesn’t just avoid an unwanted renaming. It works, as OP asked and that is all.

Good night to you too. I apologize for overreacting about the posts.

Final implementation of the script. With better approach - using Finder tags:


use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
property NSArray : a reference to NSArray of current application
property |NSURL| : a reference to NSURL of current application
property NSURLTagNamesKey : a reference to NSURLTagNamesKey of current application

on adding folder items to this_folder after receiving dropped_items
	tell application "Finder"
		set tagArray to (NSArray's arrayWithArray:{"isRenamed"})
		repeat with alias_ref in dropped_items
			
			set {aName, anExt} to {alias_ref's name, alias_ref's name extension}
			set aPath to POSIX path of alias_ref
			set anURL to (|NSURL|'s fileURLWithPath:aPath)
			
			-- Read the Finder tags of the current Finder item
			set {theResult, theTags} to (anURL's getResourceValue:(specifier) ¬
				forKey:(current application's NSURLTagNamesKey) |error|:(missing value))
			set theTagsList to theTags as list
			
			-- Check, if the current Finder item is not renamed
			if not (theTagsList is {"isRenamed"}) then
				-- Set Current Finder item's tags list to {"isRenamed"}
				(anURL's setResourceValue:tagArray forKey:NSURLTagNamesKey |error|:(missing value))
				if anExt is not "" then
					set baseName to text 1 thru -(2 + (count anExt)) of aName
					set alias_ref's name to (baseName & "_1." & anExt)
				else
					set alias_ref's name to (aName & "_1")
				end if
			end if
			
		end repeat
	end tell
end adding folder items to

Below is your script with 5 added instructions allowing us to check that it behaves as I wrote.


use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
property NSArray : a reference to NSArray of current application
property |NSURL| : a reference to NSURL of current application
property NSURLTagNamesKey : a reference to NSURLTagNamesKey of current application

on adding folder items to this_folder after receiving dropped_items
	tell application "Finder"
		set tagArray to (NSArray's arrayWithArray:{"isRenamed"})
		repeat with alias_ref in dropped_items
			set {aName, anExt} to {alias_ref's name, alias_ref's name extension}

			if aName contains "_1" then # ADDED
				tell me to say "received file named : " & aName & " which was previously renamed" # ADDED
			else # ADDED
				tell me to say "received file named : " & aName # ADDED
			end if # ADDED

			set aPath to POSIX path of alias_ref
			set anURL to (|NSURL|'s fileURLWithPath:aPath)
			
			-- Read the Finder tags of the current Finder item
			set {theResult, theTags} to (anURL's getResourceValue:(specifier) ¬
				forKey:(current application's NSURLTagNamesKey) |error|:(missing value))
			set theTagsList to theTags as list
			
			-- Check, if the current Finder item is not renamed
			if not (theTagsList is {"isRenamed"}) then
				-- Set Current Finder item's tags list to {"isRenamed"}
				(anURL's setResourceValue:tagArray forKey:NSURLTagNamesKey |error|:(missing value))
				if anExt is not "" then
					set baseName to text 1 thru -(2 + (count anExt)) of aName
					set alias_ref's name to (baseName & "_1." & anExt)
				else
					set alias_ref's name to (aName & "_1")
				end if
			end if
			
		end repeat
	end tell
end adding folder items to

When we drop a file on it, we hear:

“received file named : azerty qwerty.txt”
then we hear:
“received file named : azerty qwerty_1.txt which was previously renamed”

It’s logical because your script does exactly what I wrote:

1 - receive a file named “azerty qwerty.txt”
2 - rename it as “azerty qwerty_1.txt”
after that it :
3 - warn the system that the folder has ‘received’ the file renamed as “azerty qwerty_1.txt”
4 - will not rename it.

One ‘unwanted re-triggering after changing file nameis always issued.
We no longer have : ‘the folder action script get re-triggered again… and again …
We have : ‘the folder action script get re-triggered again

With the script moving the file in an auxiliary folder,
we get a similar behavior if the subfolder is not available at first use because the system is warned that a folder named “safeFolder” has been ‘added’ to it.
No such warning is issued if the subfolder was created before first use.
When the subfolder exists, we no longer have the ‘unwanted re-triggering’.
We just have:
1 - receive a file named “azerty qwerty.txt”
2 - move it in “safeFolder”
2 - rename the moved file as “azerty qwerty_1.txt”.
So we really don’t issue ‘unwanted re-triggering after changing file name

I may understand that you dislike to read this truth, but, believe it or not, it’s the truth.
I may also understand that the fact that your script doesn’t require a subfolder may be seen as a valid alternative to this use.
I would be glad to read ldicroce’s opinion about that.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) dimanche 22 décembre 2019 10:08:44

dear Yvan and KniazidisR,

thanks for the contributing to this.
I like a lot the idea of using tag/subfolder for avoiding further cycles of re-triggering.
I didn’t thought about that.
Thanks !

I am editing this post after reading Yvan’s comment #14.
I have to admit that both options are valid, and technically speaking Yvan’s solve the retriggering problem from the starting.
But moving files in a subfolder is not optimal for the setting I have (the Downloads folders of all my Macs get synchronised with a complicated scheme (combination of Symbolic Link-DropBox-iCloud). And a sub-folder will not be as easy ti implement initially.
I will in any case explore and test both options knowing that both are good and solve the problem i posed.
Thanks again to both!

Thank you ldicroce

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) dimanche 22 décembre 2019 11:31:16

So, Yvan Koenig is again dissatisfied with us… The second retriggering, blabla and so on. Well, I have enough that I am completely satisfied with my work. I mean my last brilliant script.

ldicroce

If you are satisfied with the KniazidisR’s version triggering the script twice, here is an enhanced version.
When it receive a file, it rename it and give it the “isRenamed” tag.
This force the system to trigger the script one more time.
At this time the received file is tagged, there is no need to rename it but it’s time to remove the tag which is no longer needed.
I made a small change because there is no need to explicitly convert a tag list into an array.

The original script is replaced by the enhanced one - preserving existing tags - available in message #20.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) lundi 23 décembre 2019 12:44:06

Thanks! I like the improvement !
Taking advantage of the re-triggering for cleaning the tag is a cool idea.
Ciao
L.

Are you sure that the dropped files have no tags or is it useful to rework the code so that it preserve existing tags in both steps ?

Here is the ‘enhanced’ version

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
property |NSURL| : a reference to NSURL of current application
property NSURLTagNamesKey : a reference to NSURLTagNamesKey of current application

on adding folder items to this_folder after receiving dropped_items
	-- two instructions used to test the code
	--set this_folder to ((path to desktop as text) & "chaud:") as alias
	--set dropped_items to {((path to desktop as text) & "chaud:jezebel_1.png") as alias}
	set isRenamed to "isRenamed"
	tell application "Finder"
		repeat with alias_ref in dropped_items
			set {aName, anExt} to {alias_ref's name, alias_ref's name extension}
			set aPath to POSIX path of alias_ref
			set anURL to (|NSURL|'s fileURLWithPath:aPath)
			
			-- Read the tags of the current Finder item
			set {theResult, theTags} to (anURL's getResourceValue:(specifier) ¬
				forKey:NSURLTagNamesKey |error|:(missing value)) # EDITED
			if theTags is missing value then # ADDED
				set theTagList to {} # ADDED
			else # ADDED
				set theTagsList to theTags as list
			end if # ADDED
			-- Check, if the current Finder item was renamed
			if theTagsList contains isRenamed then # EDITED
				set originalTags to {}
				repeat with aTag in theTagsList
					if aTag as text is not isRenamed then set end of originalTags to aTag
				end repeat
				# The unwanted re-triggering bring us here.
				# Remove the added tag which is no longer needed
				(anURL's setResourceValue:originalTags forKey:NSURLTagNamesKey |error|:(missing value)) # ADDED
			else
				# As the file was not tagged as renamed, the first triggering bring us here
				set end of theTagsList to isRenamed
				# Append isRenamed to possible existing tags
				(anURL's setResourceValue:theTagsList forKey:NSURLTagNamesKey |error|:(missing value))
				if anExt is not "" then
					text 1 thru -(2 + (count anExt)) of aName # EDITED
					set alias_ref's name to (result & "_1." & anExt) # EDITED
				else
					set alias_ref's name to (aName & "_1")
				end if
			end if
		end repeat
	end tell
end adding folder items to

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) lundi 23 décembre 2019 15:13:57

Indeed! Preserving the other tags is a plus ! Thanks again !

Here is a modified version which uses more ASObjC features.

use AppleScript version "2.4" -- El Capitan (10.11) or later
use framework "Foundation"
use scripting additions


property |NSURL| : a reference to current application's NSURL
property NSArray : a reference to current application's NSArray
property NSMutableArray : a reference to current application's NSMutableArray
property NSURLTagNamesKey : a reference to current application's NSURLTagNamesKey
--property NSNotFound : a reference to 9.22337203685477E+18 + 5807 -- get rid of a System bug

on adding folder items to this_folder after receiving dropped_items
	-- two instructions used to test the code
	-- set this_folder to ((path to desktop as text) & "oups:") as alias
	-- set dropped_items to {((path to desktop as text) & "oups:azer.png") as alias}
	
	set isRenamed to "isRenamed"
	tell application "Finder"
		repeat with alias_ref in dropped_items
			set {aName, anExt} to {alias_ref's name, alias_ref's name extension}
			if 10 < (system attribute "sys2") then #-- if running Yosemite (10.10)
				set aPath to POSIX path of alias_ref
				set anURL to (|NSURL|'s fileURLWithPath:aPath)
			else -- if running El Capitan (10.11) or higher
				set anURL to (NSArray's arrayWithObject:alias_ref)'s firstObject()
			end if
			-- Read the tags of the current Finder item
			set {theResult, theTags} to (anURL's getResourceValue:(specifier) ¬
				forKey:NSURLTagNamesKey |error|:(missing value))
			if theTags is missing value then
				set mutableArray to (NSMutableArray's new())
			else
				set mutableArray to (NSMutableArray's arrayWithArray:theTags)
			end if
			
			-- Check, if the current Finder item was renamed
			-- Edited according to Shane Stanley comment in message #23
			if (mutableArray's containsObject:isRenamed) as boolean then
				-- The unwanted re-triggering bring us here.
				-- Remove the isRenamed tag which is no longer needed
				(mutableArray's removeObject:isRenamed)
				(anURL's setResourceValue:mutableArray forKey:NSURLTagNamesKey |error|:(missing value))
			else
				-- As the file was not renamed, the first triggering bring us here
				-- Append isRenamed to the array of tags
				(mutableArray's addObject:isRenamed)
				-- Apply it
				(anURL's setResourceValue:mutableArray forKey:NSURLTagNamesKey |error|:(missing value))
				if anExt is not "" then
					text 1 thru -(2 + (count anExt)) of aName
					set alias_ref's name to (result & "_1." & anExt)
				else
					set alias_ref's name to (aName & "_1")
				end if
			end if
		end repeat
	end tell -- Finder
end adding folder items to

I don’t know if the instruction

property NSNotFound : a reference to 9.22337203685477E+18 + 5807 # get rid of a System bug

is required with Mojave and/or Catalina but, thank’s to Shane Stanley, I know that it is under High Sierra.

I’m too lazy to replace Finder dedicated instructions by ASObjC ones.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mardi 24 décembre 2019 22:06:06

It is – but that’s not the end of the story.

There are a couple of issues here. The first is that the file AppleScript uses to lookup the numeric equivalent of enums has an incorrect value for NSNotFound, returning -1. This is still an issue in Catalina, and the above is an attempt to work around it.

However, even if the file had the correct value for NSNotFound, there would still be a problem with your script. That’s because NSNotFound is equivalent to the largest 64-bit integer, and AppleScript only supports 32-bit integers (and not the full range there, either).

When a number exceeds AppleScript’s integer range, it silently changes it to a real, and reals can store a much higher range of values. The problem, however, is that when you approach more than 50-odd bit integer values, the real equivalents lack enough precision, so a certain amount of rounding occurs. Because enums are used as bitmaps, that’s disastrous.

The end result is that you can use the form above to supply NSNotFound to a method if you need to, but you can’t use it to test the result of a method, because by the time any result is returned, it’s been turned into a real and rounded.

So where you say:

           set theIndex to (mutableArray's indexOfObject:isRenamed)
           if theIndex = NSNotFound then

You’re comparing to a rounded value.

You could instead use something like:

    if (theIndex > mutableArray's |count|()) then

or:

    if (mutableArray's containsObject:isRenamed) as boolean then

Thank you Shane.

I was remembering the problem with NSNotFound but I forgot the function containsObject whose use is cleaner.
I will edit the script above.

Honestly I wouldn’t be able to imagine to use

if (theIndex > mutableArray's |count|()) then

because as I knew that the old value was -1 this comparison seems to be odd.
But after all, using

if theIndex = NSNotFound then

was odd too if the script was ran under 10.12 which is supposed to return -1 as theIndex.

What is puzzling is that when I made tests, the comparison returned the wanted result and I was able to see the tag appearing then disappearing.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mercredi 25 décembre 2019 10:55:20

The act of comparison is probably forcing the right-side value to a real, and therefore making them both equally incorrect. It’s probably a safe enough comparison to make, but it’s not something I’m entirely comfortable relying on.

Thanks Shane.

Now that I re-learnt ‘containsObject’ there is no need for a scheme which is not guaranteed.
I made some new changes because I re-discovered:

set mutableArray to (NSMutableArray's new())
(mutableArray's addObject:isRenamed)

which are cleaner than

set mutableArray to (NSMutableArray's arrayWithArray:{})
(mutableArray's insertObject:isRenamed atIndex:(0))

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mercredi 25 décembre 2019 15:00:10

Keep in mind that NSMutableArray is a subclass of NSArray, which means you can use any of NSArray’s methods too. So:

set mutableArray to (NSMutableArray's arrayWithObject:isRenamed)

Bad idea Shane.
the script was supposed to add an item to an array which was allowed to contain existing items.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) jeudi 26 décembre 2019 11:08:01

Hmmm. You wrote:

set mutableArray to (NSMutableArray's new())
(mutableArray's addObject:isRenamed)

That’s the same as:

set mutableArray to (NSMutableArray's arrayWithObject:isRenamed)

Oops. My fault.
You where fooled by the way I presented the instructions.

I would have posted


# instruction 1 - supposed to create a new array
set mutableArray to (NSMutableArray's new())
# instruction 2 - supposed to append an item to an other array which may already contain items
(mutableArray's addObject:isRenamed)

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) jeudi 26 décembre 2019 13:54:39