Create TEXT from FILENAME of IMAGES

Hello guys

I don’t know if this can be done. The problem that I am facing is this.

I have multiple huge folders, which they contain lot of images (in jpeg files). They are more than 900 images each folder. Is there a way to run a script in different folder every time (where i can choose in what folder should search) and copy the filename of each photo that can trace and paste that filename in the number (the mac application similar to excel)? I want to check with the warehouse what images I have in the folder, so I need their filenames in a list. Can this be done or it’s not possible?

Any ideas?

Thanks

I’m not sure if you are really asking for a list of filenames or for a list of pathnames.

Here is a script - built around code borrowed from Shane Stanley.

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
use script "BridgePlus"

on run
	my germaine()
end run

on germaine()
	
	load framework
	set sourceFolder to POSIX path of (choose folder)
	-- set sourceFolder to POSIX path of "Macintosh HD:Users:Important:pour ebay:" # EDIT. I forgot to disable it
	set theFileInfo to my listFilesAndUTIsIn:sourceFolder
	set theResult to my filterAnArray:theFileInfo conformingTo:"public.image"
	set theData to my concatList:theResult usingString:linefeed
	set targetFile to POSIX path of ((path to desktop as text) & "myFiles_seliFym.txt")
	
	my writeToReport(targetFile, theData, «class utf8», false)
end germaine

on listFilesAndUTIsIn:sourceFolder
	set fileManager to current application's NSFileManager's defaultManager()
	set aURL to current application's |NSURL|'s fileURLWithPath:sourceFolder
	--set directoryContents to fileManager's contentsOfDirectoryAtURL:aURL includingPropertiesForKeys:{} options:0 |error|:(missing value)
	set directoryContents to (current application's SMSForder's filesIn:sourceFolder recursive:true skipHidden:true skipInsidePackages:true asPaths:false)
	set tempArray to current application's NSMutableArray's arrayWithCapacity:(directoryContents's |count|()) -- array to hold values
	
	repeat with aURL in directoryContents
		set {theResult, theUTI} to (aURL's getResourceValue:(specifier) forKey:(current application's NSURLTypeIdentifierKey) |error|:(missing value))
		(tempArray's addObject:(current application's NSDictionary's dictionaryWithObjects:{aURL, theUTI} forKeys:{"theURL", "theUTI"}))
	end repeat
	
	return tempArray
end listFilesAndUTIsIn:

on filterAnArray:tempArray conformingTo:someUTI
	set thePredicate to current application's NSPredicate's predicateWithFormat_("theUTI UTI-CONFORMS-TO %@", someUTI)
	set foundItemList to (tempArray's filteredArrayUsingPredicate:thePredicate)'s valueForKey:"theURL"
	(*
	# Return as a list of «class furl»'s
	return foundItemList as list
	*)
	# Return as a list of POSIX Paths
	set foundItemList to (foundItemList's valueForKey:"path") as list
	return foundItemList
	
end filterAnArray:conformingTo:


#=====#=====#=====#=====#=====#=====

on concatList:theList usingString:d1
	set anArray to current application's NSArray's arrayWithArray:theList
	return (anArray's componentsJoinedByString:d1) as text
end concatList:usingString:

#=====#=====#=====#=====#=====#=====
(*
Handler borrowed to Regulus6633 - http://macscripter.net/viewtopic.php?id=36861
*)
on writeToReport(targetFile, theData, dataType, apendData)
	-- theData is the data you want in the file.
	-- dataType is the data type of theData and it can be text, list, record etc.
	-- apendData is true to append theData to the end of the current contents of the file or false to overwrite it
	
	try
		set targetFile to targetFile as «class furl»
		set openFile to open for access targetFile with write permission
		if not apendData then set eof of openFile to 0
		write theData to openFile starting at eof as dataType
		close access openFile
		return true
	on error
		try
			close access targetFile
		end try
		return false
	end try
end writeToReport

#=====#=====#=====#=====#=====#=====

It scan recursively the selected folder and return - in a text file on the desktop - a list of pathnames pointing to image files.

Oops, I forgot to write that you must install BridgePlus available - for free - at
https://www.macosxautomation.com/apples . ePlus.html

Yvan KOENIG running Sierra 10.12.1 in French (VALLAURIS, France) vendredi 18 novembre 2016 21:48:48

Shell is a really good friend for this kind of tasks. Extend or narrow the allowed list of extensions to make the script to your likings.

do shell script "ls " & quoted form of POSIX path of (choose folder) & " | egrep -i '.*(jpg|jpeg|png|gif)$' >$HOME/Desktop/filenames.csv"

Maybe not entirely unimportant, because you’re working with large folders, it is fast as wel :slight_smile:

WOW! This works like a charm. THANK YOU, THANK YOU, THANK YOU, THANK YOU, THANK YOU!!!

Oh my, you spend so much time!!! I appreciate a lot!!! I get a syntax error message “Can’t get script “BridgePlus”.” Weird!

Oops, I forgot to write that you must install BridgePlus available - for free - at
https://www.macosxautomation.com/applescript/apps/BridgePlus.html

Don’t worry, I didn’t type a lot.
I just changed one instruction in a script which was not recursive.

The original instruction is just commented out above the new one.
The original - non recursive - doesn’t require BridgePlus.

I edited the original message in which I erroneously pasted a sentence and my signature in the script part.

Yvan KOENIG running Sierra 10.12.1 in French (VALLAURIS, France) samedi 19 novembre 2016 11:13:04

NICE!!! THANKS a lot my friend. It will be so sad not working such script. Thanks a lot my friend. You are the best!

Bingo.

Here is an edited version which no longer use BridgePlus.

You may choose the way it works : scan subfolders or don’t.

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

property recursiveScan : true
# true = scan subfolders of the selected folder
# false = doesn't scan the subfolders of the selected folder

property dataMode : 3
# -1 means return relative path
# 0 mean return paths
# 1 means return names
# 2 means return path and name separated by tab character
# 3 means return relative container and filename separated by tab character


script o
	property rootLength : 0
	property delim : tab
end script

property saveAsCsv : true
# true means use the csv name extension
# false means use the txt name extension

on run
	my germaine()
end run

on germaine()
	set sourceFolder to POSIX path of (choose folder)
	
	# Starts the timer
	set startDate to current application's NSDate's |date|() -- do stuff you want to time in here, eg
	
	set theFileInfo to my listFilesAndUTIsIn:sourceFolder
	set theResult to my filterAnArray:theFileInfo conformingTo:"public.image"
	set theData to my concatList:theResult usingString:linefeed
	
	if saveAsCsv then
		if (current application's NSLocale's currentLocale()'s countryCode() as text) starts with "FR" then
			set o's delim to ";" # It's the delim used in french CSV
		else
			set o's delim to ","
		end if
		"csv"
	else
		set o's delim to tab
		"txt"
	end if
	set targetFile to POSIX path of ((path to desktop as text) & "myFiles_seliFym." & result)
	
	my writeToReport(targetFile, theData, «class utf8», false)
	
	# Grabs the end time
	set timeDiff to startDate's timeIntervalSinceNow()
	display dialog "That took " & (-timeDiff as real) & " seconds."
	
	--> non recursive, mode 0 : "That took 0,125459015369 seconds." for 243 files
	--> recursive, mode -1 : "That took 0,455298006535 seconds." for 1308 files
	--> recursive, mode 0 : "That took 0,44734197855 seconds." for 1308 files
	--> recursive, mode 1 : "That took 0,652094960213 seconds." for 1308 files
	--> recursive, mode 2 : "That took 0,742623031139 seconds." for 1308 files
	--> recursive, mode 3 : "That took 0,733532011509 seconds." for 1308 files
end germaine

on listFilesAndUTIsIn:sourceFolder
	set fileManager to current application's NSFileManager's defaultManager()
	set aURL to current application's |NSURL|'s fileURLWithPath:sourceFolder
	set (o's rootLength) to (aURL's |path|()'s |length|()) + 1 -- use to trim paths
	if recursiveScan then
		set theOptions to (current application's NSDirectoryEnumerationSkipsPackageDescendants as integer) + (current application's NSDirectoryEnumerationSkipsHiddenFiles as integer)
		set theEnumerator to fileManager's enumeratorAtURL:aURL includingPropertiesForKeys:{} options:theOptions errorHandler:(missing value)
		set directoryContents to theEnumerator's allObjects()
	else
		set directoryContents to fileManager's contentsOfDirectoryAtURL:aURL includingPropertiesForKeys:{} options:0 |error|:(missing value)
	end if
	set tempArray to current application's NSMutableArray's arrayWithCapacity:(directoryContents's |count|()) -- array to hold values
	
	repeat with aURL in directoryContents
		set {theResult, theUTI} to (aURL's getResourceValue:(reference) forKey:(current application's NSURLTypeIdentifierKey) |error|:(missing value))
		(tempArray's addObject:(current application's NSDictionary's dictionaryWithObjects:{aURL, theUTI} forKeys:{"theURL", "theUTI"}))
	end repeat
	
	return tempArray
end listFilesAndUTIsIn:

on filterAnArray:tempArray conformingTo:someUTI
	set thePredicate to current application's NSPredicate's predicateWithFormat_("theUTI UTI-CONFORMS-TO %@", someUTI)
	set foundItemList to (tempArray's filteredArrayUsingPredicate:thePredicate)'s valueForKey:"theURL"
	
	if dataMode = 0 then
		# Return as a list of POSIX Paths
		set foundItemList to (foundItemList's valueForKey:"path") as list
	else
		set newArray to current application's NSMutableArray's array() -- to store names
		
		set theCount to foundItemList's |count|()
		if dataMode = -1 then
			repeat with i from 1 to theCount
				set oneURL to (foundItemList's objectAtIndex:(i - 1)) -- zero-based indexes
				set theRelative to (oneURL's |path|()'s substringFromIndex:(o's rootLength))
				(newArray's addObject:("./" & theRelative))
			end repeat
		else if dataMode = 1 then
			# Return a list of fileNames
			repeat with i from 1 to theCount
				set oneURL to (foundItemList's objectAtIndex:(i - 1)) -- zero-based indexes
				set theName to oneURL's lastPathComponent()
				(newArray's addObject:theName)
			end repeat
		else if dataMode = 2 then
			# Return a list of couples posix path of folder containing the file and filename separated by a tab character (or a comma)
			repeat with i from 1 to theCount
				set oneURL to (foundItemList's objectAtIndex:(i - 1)) -- zero-based indexes
				set theName to oneURL's lastPathComponent()
				set theContainer to oneURL's URLByDeletingLastPathComponent()
				--set theContainer to theContainer's |path|() as text
				(newArray's addObject:((theContainer's |path|() as text) & (o's delim) & theName))
			end repeat
		else
			# Return a list of couples posix path of folder containing the file and filename separated by a tab character
			repeat with i from 1 to theCount
				set oneURL to (foundItemList's objectAtIndex:(i - 1)) -- zero-based indexes
				set theRelative to (oneURL's |path|()'s substringFromIndex:(o's rootLength))
				set theName to theRelative's lastPathComponent()
				set theRelativeContainer to theRelative's stringByDeletingLastPathComponent()
				(newArray's addObject:("./" & theRelativeContainer & (o's delim) & theName))
			end repeat
		end if
		set foundItemList to newArray as list
	end if
	
	return foundItemList
end filterAnArray:conformingTo:

#=====#=====#=====#=====#=====#=====

on concatList:theList usingString:d1
	set anArray to current application's NSArray's arrayWithArray:theList
	return (anArray's componentsJoinedByString:d1) as text
end concatList:usingString:

#=====#=====#=====#=====#=====#=====
(*
Handler borrowed to Regulus6633 - http://macscripter.net/viewtopic.php?id=36861
*)
on writeToReport(targetFile, theData, dataType, apendData)
	-- theData is the data you want in the file.
	-- dataType is the data type of theData and it can be text, list, record etc.
	-- apendData is true to append theData to the end of the current contents of the file or false to overwrite it
	
	try
		set targetFile to targetFile as «class furl»
		set openFile to open for access targetFile with write permission
		if not apendData then set eof of openFile to 0
		write theData to openFile starting at eof as dataType
		close access openFile
		return true
	on error
		try
			close access targetFile
		end try
		return false
	end try
end writeToReport

#=====#=====#=====#=====#=====#=====

As you may see, I added timer’s instructions because I wished to compare to DJ Bazzie Wazzie’s shell script.

use AppleScript version "2.4"
use scripting additions
use framework "Foundation"


set theFolder to quoted form of POSIX path of (choose folder)

set startDate to current application's NSDate's |date|() -- do stuff you want to time in here, eg

do shell script "ls " & theFolder & " | egrep -i '.*(jpg|jpeg|png|gif)$' >$HOME/Desktop/filenames.csv"

set timeDiff to startDate's timeIntervalSinceNow()
display dialog "That took " & (-timeDiff as real) & " seconds."
--> "That took 0,036794960499 seconds." for 243 files

EDIT : the added property dataMode rules the behavior of the script
added the saveAsCsv property to rule the file format

Yvan KOENIG running Sierra 10.12.1 in French (VALLAURIS, France) samedi 19 novembre 2016 11:48:56

Yvan the subfolder thing is very very useful. I like the idea, because there are lots of subfolders. How can I disable the paths in the .txt file. I mean can i have the txt report without the paths so i can copy the results in an excel file and do my stuff?

Knowing that the real time is only 1% (more like 0.0001 seconds) while the system time 99% :(. For a folder containing 722 files I can’t measure it because the overhead of the do shell script is the same time as executing the script. What it does say if that when the folder contains tens of thousand images it won’t take noticeable longer. Too bad the overhead of invoking a shell script is so heavy.

With complete paths and recursive:

do shell script "find -E " & quoted form of (text 1 thru -2 of POSIX path of (choose folder)) & " -iregex '^.*(jpg|jpeg|png|gif)$' >$HOME/Desktop/filenames.csv"

With only file names and recursive:

do shell script "find -E " & quoted form of (text 1 thru -2 of POSIX path of (choose folder)) & " -iregex '^.*(jpg|jpeg|png|gif)$' | egrep -o '[^/]*$' >$HOME/Desktop/filenames.csv"

With relative paths and recursive:

do shell script "cd " & quoted form of POSIX path of (choose folder) & "; find -E . -iregex '^.*(jpg|jpeg|png|gif)$' >$HOME/Desktop/filenames.csv"

I’m not sure that returning only the names in recursive mode is useful but as I wish to give you the choice, I edited my preceding message.

Now you may choose the behavior which fits your needs.

if datamode = 0 the script return the complete posix paths
if datamode = 1 the script return only the file names
if datamode = 2 the script return for every file : the posix path of the hosting folder and the file name separated by a tab character
Copying/pasting the late one will fill two columns in the target spreadsheet.

Yvan KOENIG running Sierra 10.12.1 in French (VALLAURIS, France) samedi 19 novembre 2016 19:30:00

Thanks man for your help.

Thanks for the feedback.

I added an alternate behavior.

For every file it returns the relative path to its container and its name separated by a tab character.

I assume that it may be the best choice for you.
In the final spreadsheet you would have the way to know where a file is located and you have its name directly available.

Yvan KOENIG running Sierra 10.12.1 in French (VALLAURIS, France) dimanche 20 novembre 2016 14:17:05