Friday, October 22, 2021

#1 2021-06-28 02:57:12 pm

peavine
Member
From:: Prescott, Arizona
Registered: 2018-09-04
Posts: 1089

Finder File Copy

I often need to copy files which are selected in a Finder window to one of a small number of frequently-used destination folders. This is easily done with a mouse but I prefer to use a keyboard, so I wrote the script contained below.  The operation of this script requires little explanation except to note that the script copies files only.

Applescript:

-- Revised 2021.07.01

use framework "AppKit"
use framework "Foundation"
use scripting additions

on main()
   set preferencesFolder to POSIX path of (path to preferences)
   set settingPlist to preferencesFolder & "FinderCopySetting.plist"
   set dataPlist to preferencesFolder & "FinderCopyData.plist"
   
   try
       set dialogDefault to settingOne of readPlist(settingPlist) as text
   on error
       set dialogDefault to "Add a Destination"
   end try
   
   try
       set destinationFolders to readPlist(dataPlist)
       set destinationNames to (destinationFolders's allKeys()'s sortedArrayUsingSelector:"caseInsensitiveCompare:") as list
   on error
       set destinationFolders to (current application's NSMutableDictionary's new())
       set destinationNames to {}
   end try
   
   set dialogMenu to destinationNames & {"--", "Add a Destination", "Delete a Destination"}
   set dialogResult to (choose from list dialogMenu with title "Finder Copy" default items dialogDefault)
   if dialogResult = false then error number -128
   set dialogResult to item 1 of dialogResult
   set destinationFolder to (destinationFolders's valueForKey:dialogResult)
   
   if dialogResult = "Add a Destination" then
       set {destinationFolders, currentName} to addDestination(destinationFolders, destinationNames)
       writePlist(dataPlist, destinationFolders)
       set dialogResult to currentName
   else if dialogResult = "Delete a Destination" then
       set destinationFolders to deleteDestination(destinationFolders, destinationNames)
       writePlist(dataPlist, destinationFolders)
   else if dialogResult begins with "-" then
       error number -128
   else
       copyOne(destinationFolder)
   end if
   
   set theSetting to current application's NSDictionary's dictionaryWithDictionary:{settingOne:dialogResult}
   writePlist(settingPlist, theSetting)
end main

on addDestination(destinationFolders, destinationNames)
   try
       tell application "Finder"
           set currentName to name of Finder window 1
           set currentPath to target of Finder window 1 as alias
       end tell
       set currentPath to POSIX path of currentPath
   on error
       errorDialog("Add a Destination", "A Finder window was not found or the target of the front Finder window cannot be a destination")
   end try
   
   set dialogPath to (current application's NSString's stringWithString:currentPath)'s pathComponents()
   if (count dialogPath) < 7 then
       set dialogPath to text 1 thru -2 of currentPath
   else
       set dialogPath to current application's NSString's stringWithFormat_("/%@/%@/.../%@/%@", item 2 of dialogPath, item 3 of dialogPath, item -3 of dialogPath, item -2 of dialogPath) as text
   end if
   
   set currentName to text returned of (display dialog "Destination name for " & quote & dialogPath & quote default answer currentName buttons {"Cancel", "OK"} default button 2 cancel button 1 with title "Finder Copy")
   if currentName = "" then error number -128
   if currentName is in destinationNames then errorDialog("Add a Destination", "A destination named " & quote & currentName & quote & " already exists")
   
   destinationFolders's setObject:currentPath forKey:currentName
   return {destinationFolders, currentName}
end addDestination

on deleteDestination(destinationFolders, destinationNames)
   if destinationNames = {} then errorDialog("Delete a Destination", "There are no destinations to delete")
   
   set deleteItems to (choose from list destinationNames with title "Finder Destination" with prompt "Select destinations to delete:" default items {item 1 of destinationNames} with multiple selections allowed)
   if deleteItems = false then error number -128
   
   set deleteItems to current application's NSArray's arrayWithArray:deleteItems
   (destinationFolders's removeObjectsForKeys:deleteItems)
   return destinationFolders
end deleteDestination

on copyOne(thePath)
   tell application "Finder" to set selectedFiles to selection as alias list
   if selectedFiles = {} then errorDialog("Finder Copy", "File not selected or selected item cannot be copied")
   set fileManager to current application's NSFileManager's defaultManager()
   
   repeat with aFile in selectedFiles
       copyTwo(POSIX path of aFile, thePath, fileManager)
   end repeat
end copyOne

on copyTwo(aFile, thePath, fileManager)
   set aFileURL to (current application's |NSURL|'s fileURLWithPath:aFile)
   set aFileString to (current application's NSString's stringWithString:aFile)
   set aFileName to aFileString's lastPathComponent()
   set aNewFile to (thePath's stringByAppendingPathComponent:aFileName)
   if (aFileString's isEqual:aNewFile) then errorDialog("File Copy", "The source and destination folders are the same")
   
   set {theResult, isDirectory} to (aFileURL's getResourceValue:(reference) forKey:(current application's NSURLIsDirectoryKey) |error|:(missing value))
   set {theResult, isPackage} to (aFileURL's getResourceValue:(reference) forKey:(current application's NSURLIsPackageKey) |error|:(missing value))
   if isDirectory as boolean = true and isPackage as boolean = false then
       display alert "Finder Copy" message quote & aFileName & quote & " is a folder" buttons {"Cancel", "Skip"} default button 2 cancel button 1 as critical
       return
   end if
   
   if (fileManager's fileExistsAtPath:aNewFile) then
       display alert "Finder Copy" message "The destination already contains " & quote & aFileName & quote buttons {"Cancel", "Skip", "Overwrite"} default button 2 cancel button 1 as critical
       if button returned of result = "Skip" then return
       set aNewFileURL to current application's |NSURL|'s fileURLWithPath:aNewFile
       (fileManager's trashItemAtURL:aNewFileURL resultingItemURL:(missing value) |error|:(missing value))
   end if
   
   set theResult to (fileManager's copyItemAtPath:aFileString toPath:aNewFile |error|:(missing value))
   if (theResult as boolean) is false then errorDialog("File Copy", "An error occurred while copying " & quote & aFileName & quote)
end copyTwo

on readPlist(thePath)
   return (current application's NSMutableDictionary's dictionaryWithContentsOfFile:thePath)
end readPlist

on writePlist(thePath, theDictionary)
   theDictionary's writeToFile:thePath atomically:true
end writePlist

on errorDialog(textOne, textTwo)
   display alert textOne message textTwo buttons {"OK"} default button 1 as critical
   error number -128
end errorDialog

main()

Last edited by peavine (2021-07-01 09:58:30 am)


2018 Mac mini - macOS Catalina - Script Debugger 8

Offline

 

#2 2021-06-29 11:47:44 am

One208
Member
Registered: 2021-03-15
Posts: 32

Re: Finder File Copy

Hi peavine
I use your other script Bookmark Finder windows too

display alert "Finder Copy" message "The destination already contains "
"Cancel", "Skip", "Overwrite"


wondering if the script be modified with Another  option "keep both files" if it already exists in target folder, renaming it (cud be appending 1 or similar)

And if possible to move & merge folders if they exist at Destination
 
Cheers

Offline

 

#3 2021-06-29 07:00:56 pm

peavine
Member
From:: Prescott, Arizona
Registered: 2018-09-04
Posts: 1089

Re: Finder File Copy

One208. Thanks for looking at my script and for the suggestions.

Replacing the skip option with a keep-both option makes sense and, in retrospect, would be my preference. I'll work on that and post a new script here when done.


2018 Mac mini - macOS Catalina - Script Debugger 8

Offline

 

#4 2021-06-30 02:04:18 pm

peavine
Member
From:: Prescott, Arizona
Registered: 2018-09-04
Posts: 1089

Re: Finder File Copy

The following script is similar to the one in post 1, differing only in that the option to skip an existing file has been replaced with an option to keep both files. When this option is selected, the newly-created file contains a counter, the format of which is set by the following code. This is easily changed to whatever the user prefers.

Applescript:

set fileCounter to " - copy " & (i as text)

I have tested both of my scripts at some length. but please have a reliable backup of all files during your testing of either of these scripts.

Applescript:

-- Revised 2021.07.03

use framework "AppKit"
use framework "Foundation"
use scripting additions

on main()
   set preferencesFolder to POSIX path of (path to preferences)
   set settingPlist to preferencesFolder & "FinderCopySetting.plist"
   set dataPlist to preferencesFolder & "FinderCopyData.plist"
   
   try
       set dialogDefault to settingOne of readPlist(settingPlist) as text
   on error
       set dialogDefault to "Add a Destination"
   end try
   
   try
       set destinationFolders to readPlist(dataPlist)
       set destinationNames to (destinationFolders's allKeys()'s sortedArrayUsingSelector:"caseInsensitiveCompare:") as list
   on error
       set destinationFolders to (current application's NSMutableDictionary's new())
       set destinationNames to {}
   end try
   
   set dialogMenu to destinationNames & {"--", "Add a Destination", "Delete a Destination"}
   set dialogResult to (choose from list dialogMenu with title "Finder Copy" default items dialogDefault)
   if dialogResult = false then error number -128
   set dialogResult to item 1 of dialogResult
   set destinationFolder to (destinationFolders's valueForKey:dialogResult)
   
   if dialogResult = "Add a Destination" then
       set {destinationFolders, currentName} to addDestination(destinationFolders, destinationNames)
       writePlist(dataPlist, destinationFolders)
       set dialogResult to currentName
   else if dialogResult = "Delete a Destination" then
       set destinationFolders to deleteDestination(destinationFolders, destinationNames)
       writePlist(dataPlist, destinationFolders)
   else if dialogResult begins with "-" then
       error number -128
   else
       copyOne(destinationFolder)
   end if
   
   set theSetting to current application's NSDictionary's dictionaryWithDictionary:{settingOne:dialogResult}
   writePlist(settingPlist, theSetting)
end main

on addDestination(destinationFolders, destinationNames)
   try
       tell application "Finder"
           set currentName to name of Finder window 1
           set currentPath to target of Finder window 1 as alias
       end tell
       set currentPath to POSIX path of currentPath
   on error
       errorDialog("Add a Destination", "A Finder window was not found or the target of the front Finder window cannot be a destination")
   end try
   
   set dialogPath to (current application's NSString's stringWithString:currentPath)'s pathComponents()
   if (count dialogPath) < 7 then
       set dialogPath to text 1 thru -2 of currentPath
   else
       set dialogPath to current application's NSString's stringWithFormat_("/%@/%@/.../%@/%@", item 2 of dialogPath, item 3 of dialogPath, item -3 of dialogPath, item -2 of dialogPath) as text
   end if
   
   set currentName to text returned of (display dialog "Destination name for " & quote & dialogPath & quote default answer currentName buttons {"Cancel", "OK"} default button 2 cancel button 1 with title "Finder Copy")
   if currentName = "" then error number -128
   if currentName is in destinationNames then errorDialog("Add a Destination", "A destination named " & quote & currentName & quote & " already exists")
   
   destinationFolders's setObject:currentPath forKey:currentName
   return {destinationFolders, currentName}
end addDestination

on deleteDestination(destinationFolders, destinationNames)
   if destinationNames = {} then errorDialog("Delete a Destination", "There are no destinations to delete")
   
   set deleteItems to (choose from list destinationNames with title "Finder Destination" with prompt "Select destinations to delete:" default items {item 1 of destinationNames} with multiple selections allowed)
   if deleteItems = false then error number -128
   
   set deleteItems to current application's NSArray's arrayWithArray:deleteItems
   (destinationFolders's removeObjectsForKeys:deleteItems)
   return destinationFolders
end deleteDestination

on copyOne(thePath)
   tell application "Finder" to set selectedFiles to selection as alias list
   if selectedFiles = {} then errorDialog("Finder Copy", "File not selected or selected item cannot be copied")
   set fileManager to current application's NSFileManager's defaultManager()
   
   repeat with aFile in selectedFiles
       copyTwo(POSIX path of aFile, thePath, fileManager)
   end repeat
end copyOne

on copyTwo(aFile, thePath, fileManager)
   set aFileURL to (current application's |NSURL|'s fileURLWithPath:aFile)
   set aFileString to (current application's NSString's stringWithString:aFile)
   set aFileName to aFileString's lastPathComponent()
   set aNewFile to (thePath's stringByAppendingPathComponent:aFileName)
   
   if (aFileString's isEqual:aNewFile) then errorDialog("File Copy", "The source and destination folders are the same")
   
   set {theResult, isDirectory} to (aFileURL's getResourceValue:(reference) forKey:(current application's NSURLIsDirectoryKey) |error|:(missing value))
   set {theResult, isPackage} to (aFileURL's getResourceValue:(reference) forKey:(current application's NSURLIsPackageKey) |error|:(missing value))
   if isDirectory as boolean = true and isPackage as boolean = false then
       display alert "Finder Copy" message quote & aFileName & quote & " is a folder" buttons {"Cancel", "Skip"} cancel button 1 default button 2 as warning
       return
   end if
   
   if (fileManager's fileExistsAtPath:aNewFile) then
       display alert "Finder Copy" message "The destination already contains " & quote & aFileName & quote buttons {"Cancel", "Replace", "Keep Both"} cancel button 1 default button 3 as warning
       if button returned of result = "Keep Both" then
           set aNewFile to makeNewFileName(aNewFile, fileManager)
       else
           fileManager's trashItemAtURL:(current application's |NSURL|'s fileURLWithPath:aNewFile) resultingItemURL:(missing value) |error|:(missing value)
       end if
   end if
   
   set theResult to (fileManager's copyItemAtPath:aFileString toPath:aNewFile |error|:(missing value))
   if (theResult as boolean) is false then errorDialog("File Copy", "An error occurred while copying " & quote & aFileName & quote & ". Please vertify that the destination folder exists.")
end copyTwo

on makeNewFileName(theFile, fileManager)
   set fileBase to theFile's stringByDeletingPathExtension()
   set fileExtension to theFile's pathExtension()
   repeat with i from 1 to 100
       set fileCounter to " - copy " & (i as text)
       set newFile to ((fileBase's stringByAppendingString:fileCounter)'s stringByAppendingPathExtension:fileExtension)
       if not (fileManager's fileExistsAtPath:newFile) then return newFile
   end repeat
end makeNewFileName

on readPlist(thePath)
   return (current application's NSMutableDictionary's dictionaryWithContentsOfFile:thePath)
end readPlist

on writePlist(thePath, theDictionary)
   theDictionary's writeToFile:thePath atomically:true
end writePlist

on errorDialog(textOne, textTwo)
   display alert textOne message textTwo buttons {"OK"} default button 1 as critical
   error number -128
end errorDialog

main()

Last edited by peavine (2021-07-04 07:16:41 am)


2018 Mac mini - macOS Catalina - Script Debugger 8

Offline

 

#5 2021-06-30 03:49:38 pm

peavine
Member
From:: Prescott, Arizona
Registered: 2018-09-04
Posts: 1089

Re: Finder File Copy

A few final comments:

* I have tested my scripts with macOS Catalina only.

* When a file exists in the destination folder, the user is presented with the option to overwrite that file. This is not actually what happens, as the existing file is instead moved to the trash, which is done to reduce the possibility of data loss if something goes awry.

* When the script moves a file to the trash, as explained above, and if the user needs to restore that file, the "put back" option is often missing or does not work as expected. The trashed file can instead be moved with a mouse to whatever folder the user wants.

* My scripts can be made to move rather than copy selected files by changing copyItemAtPath:aFileString to moveItemAtPath:aFileString. In **very preliminary testing** this appears to work as expected.

Last edited by peavine (2021-07-02 08:05:22 am)


2018 Mac mini - macOS Catalina - Script Debugger 8

Offline

 

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)