Generic display of Spotlight Metadata for file(s)

Hello!

Select some files in finder, run the script, and obtain the metadata attributes for the file(s).

The metadata attributes may vary with the filetype, and this script reflects that.

It is all stolen, and compiled by me, which is the spelled compilare in latin and means plunder! :smiley:



-- http://macscripter.net/viewtopic.php?id=39265
--  SPOTLIGHT INFO FOR SELECTED ITEMS
-- PARTS ©1998 Sal Soghoian, Apple Computer
-- PARTS © yiam-jin-qui http://macscripter.net/viewtopic.php?id=36842
-- Parts © 2012 McUsr I'd rather have you referring to this post at Macscripter.net http://macscripter.net/edit.php?id=154116
-- than posting it elsewhere!
(*
	Compiled  By McUsr 09/08/12:
	Based upen INFO FOR SELECTED Items, uses mouramartins convert mdlsinfo (spotlight metadata to list).
*)
property recIdList : missing value
property genericIcon : a reference to file ((path to library folder from system domain as Unicode text) & "CoreServices:CoreTypes.bundle:Contents:Resources:GenericEditionFileIcon.icns")
property appTitle : "Spotlight Meta Data for Items info"
set ReportText to ""
try
	
	tell application "Finder"
		activate
		set selected_items_list to (get selection) as alias list
		
		set selCount to count selected_items_list
		
		if selCount ñ‰  0 then set last_item to the last item of the selected_items_list as text
		
		set startPath to target of its Finder window 1 as alias
	end tell
	
	
	if selCount = 0 then
		tell me
			activate
			
			set the selected_items_list to (choose file with prompt "Choose the files or folders you want to see info for for" default location startPath with multiple selections allowed)
			if the (count of the selected_items_list) is 0 then
				with timeout of 900 seconds
					beep
					display dialog "No files or applications are selected." buttons {"Cancel"} default button 1
				end timeout
			end if
			set last_item to the last item of the selected_items_list as text
		end tell
	end if
	tell me
		activate
		try
			set outputType to button returned of (display dialog "Please choose form of output" with title appTitle buttons {"Cancel", "Report", "Dialogs"} cancel button 1 default button 3 with icon genericIcon)
		on error
			beep
			error number -128
		end try
	end tell
	
	repeat with this_item in the selected_items_list
		if outputType is "Dialogs" then
			set ReportText to ""
		end if
		tell application "Finder" to set item_name to name of this_item
		
		
		set filepath to quoted form of POSIX path of (this_item as alias)
		set metaRec to metaDataRecord for filepath
		
		if outputType is "Report" then
			set ReportText to ReportText & return & "==================================" & return & "Name: " & item_name & return
		else
			set ReportText to ReportText & return & "Name: " & item_name & return
		end if
		
		-- set recIdList to get user property names metaRec
		set my recIdList to Rec2UserKeyValues(metaRec)
		
		repeat with i from 1 to (count recIdList)
			
			set ReportText to ReportText & item 1 of item i of my recIdList & ": "
			if class of item 2 of item i of my recIdList = list then
				set oltids to AppleScript's text item delimiters
				set AppleScript's text item delimiters to "\" , \""
				set tmpTxt to item 2 of item i of my recIdList as text
				set AppleScript's text item delimiters to oltids
				set ReportText to ReportText & "{ \"" & tmpTxt & "\" }" & return
			else
				set ReportText to ReportText & item 2 of item i of my recIdList & return
			end if
		end repeat
		
		
		if outputType is "Dialogs" then
			set the clipboard to ReportText
			if this_item is the last_item or selCount is 1 then
				set the button_list to {"Done"}
			else
				set the button_list to {"Cancel", "Next"}
			end if
			-- display the information
			with timeout of 900 seconds
				tell me
					activate
					
					display dialog ReportText with title appTitle buttons the button_list default button (the last item of the button_list)
				end tell
			end timeout
		end if
	end repeat
	if outputType is "Report" then
		
		tell application "TextEdit"
			activate
			make new document at the front
			set text of front document to "F i l e   i n f o r m a t i o n " & return & ReportText
		end tell
	end if
on error e number n
	if n is not -128 then
		display dialog "error : " & e & n
	end if
end try

to metaDataRecord for pxFilepath
	--  mouramartins Macscripter.net http://macscripter.net/viewtopic.php?id=39249
	local rs, prs, k, islist, p, x, i
	
	set islist to false
	set rs to (do shell script "mdls " & pxFilepath)
	set prs to {}
	repeat with k from 1 to count of paragraphs of rs
		set p to paragraph k of rs
		if islist then
			if text 1 of p is ")" then
				set islist to false
				set x to x & "}"
				
				try
					set prs to prs & (run script "{" & x & "}")
				end try
			else
				set x to x & p
			end if
		else
			set i to offset of "=" in p
			if i > 1 then
				set x to (text 1 thru (i - 1) of p) & ":"
				if text (i + 2) of p = "(" then
					set x to x & "{"
					set islist to true
				else
					set t to text (i + 2) thru -1 of p as text
					try
						set prs to prs & (run script "{" & x & t & "}")
					on error
						set prs to prs & (run script "{" & x & "\"" & t & "\"" & "}")
					end try
				end if
			end if
			
		end if
	end repeat
	return prs
end metaDataRecord



on Rec2UserKeyValues(recAny)
	-- http://macscripter.net/viewtopic.php?id=36842 yiam-jin-qui
	-- USE THE CLIPBOARD TO MAKE THE RECORD KEYS LEGIBLE
	set the clipboard to recAny
	set recLegible to (the clipboard as record)
	set lngPairs to count of (recAny as list)
	if lngPairs < 1 then return {}
	
	-- COLLECT ANY USER-DEFINED KEY-VALUE PAIRS
	set lstKeyValue to {}
	try
		set lstUser to list of recLegible
	on error
		display dialog (do shell script "osascript -e 'the clipboard as record'") buttons "OK" default button 1 with title "Contents of record"
		return {}
	end try
	
	repeat with i from 1 to (length of lstUser) - 1 by 2
		set end of lstKeyValue to {item i of lstUser, item (i + 1) of lstUser}
	end repeat
	
	-- IF ANY PAIRS ARE MISSING, TRY SOME SYSTEM-DEFINED KEYNAMES
	if (count of lstKeyValue) < lngPairs then
		try
			set beginning of lstKeyValue to {"Date", date of recAny}
		end try
		try
			set beginning of lstKeyValue to {"Name", name of recAny}
		end try
	end if
	lstKeyValue
end Rec2UserKeyValues


Hehe, thanks for the plundering! :lol:
But you can edit out the ©, that’s sooo 1800’s, this is the XXIst Century.

Unfortunately, since I had to fish the snippet from the middle of of the “to do” pile, I found out it’s horribly slow. While eating a pizza today, I found the culprit:

                       set prs to prs & (run script "{" & x & t & "}")

The solution: build the whole string and “run script” once in the end, or once per file.

The pizza ruined my keyboard so it’s still in the to do pile. While the snippet could solve the problem at hand it is a pain as a generic

Hello!

What if I like old fashioned stuff? :slight_smile:

But If you want that, to remove that notice, then I’ll offcourse acquiesce to your request.

As for the run script, it works now, and I have really a lot on my todo list as well! You really wouldn’t believe.

If I can make the change in 10 minutes, I will!

Edit

I really can’t fix that in 10 minutes, as I really have to consider the sublists here, which are needed, in order to make that record!

I’ll give it some more thought, and come back to it!

If you know how to implement the changes to make it go faster, be my guest! :slight_smile:

I hope your keyboard surives! Mine is about worn out! :frowning:

still soiled with peperoni and cheese: (that’s a warning!)


property knownItems : {kmditemaudiochannelcount:missing value, kMDItemAudioEncodingApplication:missing value, kmditemaudiosamplerate:missing value, kmditemaudiotracknumber:missing value, kmditemauthors:{}, kMDItemComment:missing value, kMDItemComposer:missing value, kMDItemCopyright:missing value, kMDItemFinderComment:missing value, kMDItemIsGeneralMIDISequence:missing value, kMDItemKeySignature:missing value, kMDItemKeywords:{}, kMDItemLyricist:missing value, kmditemmusicalgenre:missing value, kMDItemOriginalFormat:missing value, kMDItemOriginalSource:missing value, kMDItemProducer:missing value, kMDItemRecordingDate:missing value, kmditemrecordingyear:missing value, kMDItemTempo:missing value, kMDItemTimeSignature:missing value, kmditemtitle:missing value}

property info : {}

on open of theFiles
	--	Executed when files are dropped on the script
	set info to {}
	repeat with f in theFiles
		set fp to quoted form of POSIX path of f
		set rs to (do shell script "mdls " & fp)
		set prs to "{"
		set islist to false
		set isdate to false
		(*
		eg:
		kMDItemUsedDates               = (
	    2012-08-09 00:00:00 +0100
		)
*)
		repeat with k from 1 to count of paragraphs of rs
			set p to paragraph k of rs
			if islist then
				if text 1 of p is ")" then
					set islist to false
					set x to x & "},"
					set prs to prs & x
					set isdate to false
				else
					if isdate then
						set x to x & "date \"" & getdate(p) & "\""
						if last character of p = "," then
							set x to x & ","
						end if
					else
						set x to x & p
					end if
				end if
			else
				set i to offset of "=" in p
				if i > 1 then
					set x to (text 1 thru (i - 1) of p) & ":"
					if text (i + 2) of p = "(" then
						set x to x & "{"
						set islist to true
						if x contains "date" then
							set isdate to true
						end if
						
					else
						if x contains "date" then
							set x to x & "date \"" & getdate(text (i + 2) thru -1 of p) & "\""
						else
							set x to x & text (i + 2) thru -1 of p
						end if
						set prs to prs & x & ","
					end if
				end if
				
			end if
		end repeat
		set prs to text 1 thru -2 of prs & "}"
		set prs to (run script prs)
		copy (prs & knownItems) to end of info
	end repeat
end open


on getdate(t)
	--	    2012-08-09 00:00:00 +0100
	set d to every word of t
	set r to current date
	set year of r to item 1 of d
	set month of r to item 2 of d
	set day of r to item 3 of d
	set time of r to ((item 4 of d) * hours + (item 5 of d) * minutes + (item 6 of d))

-- for the sake of completeness, can be omited
	set tz to 1 * (item 8 of d)
	if item 7 of d = "-" then set tz to -tz
	set tz to hours * (tz div 100) + minutes * tz mod 100
	set r to r + (time to GMT) - tz
-- end of completeness sake

	return (short date string of r & " " & time string of r)
	-- portuguese for example flips with date as text
end getdate

Nice!

I haven’t played with it yet, as I am just up from sleep.

The solution I presented over, is however slow, but generic in the sense, that it will put do display any KMDIitem of a file, no more, no less. I am also reluctant to keeping all the data as is, as it is just to look at anyway. But there is no problem in making two versions, or having an attribute set to make it behave differently!

My objective is to use this for different kinds of stuff, like viewing the KDMitems for playing with mdfind for instance, but also to pick files based on the attributes in the spotlight metadata. I realize your objectives may differ slightly, or that you have specific usage in mind, that I haven’t foreseen yet!

I think, seeing now, how you approach it, with pepperoni, and cheese, that it is totally possible to work something out!

Tomorrow, is yet another day . and never dies! :slight_smile:

I can’t sleep!

And I have an idea, you will like! It will speed up things considerably! At least what a “genereic” solution concerns.

I hope you like the idea as well:

Instead of generating a record of attributes, I will make a list with key value pairs. Some itmes, containing a list within them. So that, I for instance, for my needs kan just display the list with key value pairs, without any creating, and disassembly of the record afterwards!

Because it is quite easy to create a record anyway, when the whole list with key value pairs are constructed anyway.

That way, there is no unecessary operations, the Metadata is also easy to display, and we can create the record easily, when we need it!

I am not fond of tinkering with the dates, until after the exctraction of the data, so that the meta data is extracted as is, as we may have different needs. There should however be no problems in converting the textual data into date objects and such later on I think. I think that will even be faster, as it is then to just test for the properties containing dates, and go directly on them, instead of testing for them at all times.

I’d love to hear your opinons about this!

Going back to bed!

Maybe something usefull in my ‘old’ box of code. This parses the result of mdls into an AS record using AWK.

set theText to do shell script "mdls /path/to/file  | awk '{
if ($3 ~ /\\($/){ 
	printf \"%s:{\", $1; 
	getline; 
	while($1!=\")\"){
		if ($1 ~ /^\\\".*/){
			printf \"%s\", $0;
		} else {
			if ($1 ~ /,$/){
				printf(\"\\\"%s\\\", \", substr($1, 0, length($1)-1) );
			} else {
				printf(\"\\\"%s\\\"\", $1);
			}
		}
		getline;
	}
	print \"}\";
} else if ($3 ~ /^\\\".*/){
	printf(\"%s:\", $1);
	for (i = 3; i <= NF; i++) {
		printf(\"%s \", $i); 
	}
	print \" \";
} else {
	printf \"%s %s \\\"%s\\\"\\" & linefeed & "\", $1, \":\", $3
}}'"

set AppleScript's text item delimiters to ","
set theText to every paragraph of theText as string
set AppleScript's text item delimiters to ""

set theRecord to run script ("{" & theText & "}")

Creating a record is easy with the output from mdls, at least the label part, as long as “run script” is not called every line, like in the original post. Once per file is perfectly acceptable. Since I will need this snippet soon, it’s almost done now. In the main part you can find the refactoring from “run script” once per line to once per file

The date thing showed up as a problem because they are not quoted in the output and thus raise an error. Numbers are fine and end up of class number and all is well. For the purposes of later conversion the routine is there if anyone needs it.

if x contains "date" 

is not generic at all and WILL fail. The generic solution: on error add quotes as your program does. I think the output can only be string, number, boolean (as number, and so far I only saw as quoted number (“0”, “1”) and date in this particular format. This implies some other test for the presence of a date. Whatever the test, much faster then “run script”

Fail. Oh well


Gobbles some words, occasionally adds extra quotes.

Lines like
else if ($3 ~ /^\".*\"$/){
are the reason I use AppleScript :rolleyes:.

Ah! Breakfast. I go order a pizza.

Tell me where please or at least which file kind? It works well on my machine without any problems. I almost never use this because I almost never need file information at all but found it in my big box of code and thought it would be worth it to show. Also the intention was to show that there are more ways to skin a cat.

Maybe it seems difficult, just depends which syntax you prefer, but you’re already in the shell. The more you can prepare the data, to handle in AS easier, the better it is. I know mdls is a slow command so that awk is hundreds of times faster that your script isn’t really a selling point but the data is created for the shell so I prefer to handle it by the shell as well.

Hello!

This works well for returning a list, not so well, to coerce it to a record! :slight_smile:

Well, It is perfectly easy to do something with the dates, and return a record, so that is the next thing up.

The one thing I haven’t tried, is to write a list, and read back a record, and . there is some overhead in doing that.
Actually, I botched the coercing to a record, as soon as I saw it failed, after having removed escaped strings.

I guess we’ll end up with two separate handlers here, or one that returns either class list or class record. :slight_smile:

Edit Unless someone cunning shows up with their tricks


to metaDataList for pxFilepath
	--  mouramartins Macscripter.net http://macscripter.net/viewtopic.php?id=39249
	local rs, prs, k, islist, p, x, i, titem, litem, y, z
	
	set islist to false
	set rs to (do shell script "mdls " & pxFilepath)
	set prs to {}
	repeat with k from 1 to count of paragraphs of rs
		set p to paragraph k of rs
		if islist then
			if text 1 of p is ")" then
				set islist to false
				set end of titem to litem
				set end of prs to titem
			else
				set y to offset of "\"" in p
				set z to (text (y + 1) thru -1 of p)
				set y to offset of "\"" in z
				set p to (text 1 thru (y - 1) of z)
				set end of litem to p
			end if
		else -- it is a new item
			
			set i to offset of "=" in p
			if i > 1 then
				set titem to {}
				set x to (text 1 thru (i - 1) of p)
				-- clean up x
				set y to offset of " " in x
				set z to (text 1 thru (y - 1) of x)
				if islist then
					set end of litem to z
				else
					set end of titem to z
				end if
				if text (i + 2) of p = "(" then
					set litem to {}
					
					set islist to true
				else
					set x to text (i + 2) thru -1 of p as text
					
					set y to offset of "\"" in x
					if y > 0 then
						set z to (text (y + 1) thru -1 of x)
						set y to offset of "\"" in z
						if (y - 1) > 0 then
							set x to (text 1 thru (y - 1) of z)
						else
							set x to ""
						end if
					end if
					set end of titem to x
					set end of prs to titem
				end if
			end if
		end if
	end repeat
	return prs
end metaDataList

Breakfast seems like a good idea! :slight_smile:

Hehe, I understood your intentions and tried it in the hope that it might be useful. But I am completely baffled even by grep.

The output from mdls:


kMDItemAlbum                   = "Le Gal"
kMDItemAudioChannelCount       = 2
kMDItemAudioSampleRate         = 44100
kMDItemAudioTrackNumber        = "04"
kMDItemAuthors                 = (
    "Gal Costa"
)
kMDItemBitsPerSample           = 16
kMDItemComposer                = ""
kMDItemContentCreationDate     = 2012-08-06 20:01:49 +0100
kMDItemContentModificationDate = 2012-08-09 14:28:44 +0100
kMDItemContentType             = "org.perian.flac"
kMDItemContentTypeTree         = (
    "org.perian.flac",
    "public.data",
    "public.item",
    "public.content",
    "public.audio",
    "public.audiovisual-content"
)
kMDItemDisplayName             = "(4) Mini-Misterio - Gal Costa.flac"
kMDItemDurationSeconds         = 259
kMDItemFSContentChangeDate     = 2012-08-09 14:28:44 +0100
kMDItemFSCreationDate          = 2012-08-06 20:01:49 +0100
kMDItemFSCreatorCode           = ""
kMDItemFSFinderFlags           = 0
kMDItemFSHasCustomIcon         = 0
kMDItemFSInvisible             = 0
kMDItemFSIsExtensionHidden     = 0
kMDItemFSIsStationery          = 0
kMDItemFSLabel                 = 0
kMDItemFSName                  = "(4) Mini-Misterio - Gal Costa.flac"
kMDItemFSNodeCount             = 0
kMDItemFSOwnerGroupID          = 20
kMDItemFSOwnerUserID           = 502
kMDItemFSSize                  = 24806892
kMDItemFSTypeCode              = ""
kMDItemKind                    = "FLAC Audio"
kMDItemLastUsedDate            = 2012-08-10 01:50:02 +0100
kMDItemMediaTypes              = (
    Sound
)
kMDItemMusicalGenre            = "MPB"
kMDItemRecordingYear           = "1970"
kMDItemTitle                   = "Mini-Mistério"
kMDItemUsedDates               = (
    2012-08-09 00:00:00 +0100,
    2012-08-10 00:00:00 +0100
)
org_xiph_flac_compilation      = "0"
org_xiph_flac_discNumber       = "1"

theText, as given to run script:
Note kMDItemAlbum and the occurence of extra quotes, eg in kMDItemKind : "“FLAC”

{kMDItemAlbum : ""Le",kMDItemAudioChannelCount : "2",kMDItemAudioSampleRate : "44100",kMDItemAudioTrackNumber:"04",kMDItemAuthors:{    "Gal Costa"},kMDItemBitsPerSample : "16",kMDItemComposer:"",kMDItemContentCreationDate : "2012-08-06",kMDItemContentModificationDate : "2012-08-09",kMDItemContentType:"org.perian.flac",kMDItemContentTypeTree:{    "org.perian.flac",    "public.data",    "public.item",    "public.content",    "public.audio",    "public.audiovisual-content"},kMDItemDisplayName : ""(4)",kMDItemDurationSeconds : "259",kMDItemFSContentChangeDate : "2012-08-09",kMDItemFSCreationDate : "2012-08-06",kMDItemFSCreatorCode:"",kMDItemFSFinderFlags : "0",kMDItemFSHasCustomIcon : "0",kMDItemFSInvisible : "0",kMDItemFSIsExtensionHidden : "0",kMDItemFSIsStationery : "0",kMDItemFSLabel : "0",kMDItemFSName : ""(4)",kMDItemFSNodeCount : "0",kMDItemFSOwnerGroupID : "20",kMDItemFSOwnerUserID : "502",kMDItemFSSize : "24806892",kMDItemFSTypeCode:"",kMDItemKind : ""FLAC",kMDItemLastUsedDate : "2012-08-10",kMDItemMediaTypes:{    Sound},kMDItemMusicalGenre:"MPB",kMDItemRecordingYear:"1970",kMDItemTitle:"Mini-Mistério",kMDItemUsedDates:{    2012-08-09 00:00:00 +0100,    2012-08-10 00:00:00 +0100},org_xiph_flac_compilation:"0",org_xiph_flac_discNumber:"1"}

Hello!

Here is an updated version of the original script, see if this works for you, while I work on the record version.

Editing during “breakfast”
I’d love to have a look into your box of code Bazzie Wazzie! :slight_smile: And I’ll look into your routine, but for now, I’ll have to postpone it.

The routine below should be considerably faster than the old one, I don’t bother to perform timing on this. I can optimize it further by implementing at least three tweaks. But I am not sure if it is necessary!


-- http://macscripter.net/edit.php?id=154143
-- New faster way to create record by Nigel Garvey

-- http://macscripter.net/viewtopic.php?id=39265
--  SPOTLIGHT INFO FOR SELECTED ITEMS
-- PARTS ©1998 Sal Soghoian, Apple Computer
-- Parts © 2012 McUsr I'd rather have you referring to this post at Macscripter.net http://macscripter.net/edit.php?id=154116
-- than posting it elsewhere!
(*
	Compiled  By McUsr 09/08/12:
	Based upen INFO FOR SELECTED Items, uses mouramartins convert mdlsinfo (spotlight metadata to list).
	New code converts mdls info to a list directly
*)
property metaList : missing value
property genericIcon : a reference to file ((path to library folder from system domain as Unicode text) & "CoreServices:CoreTypes.bundle:Contents:Resources:GenericEditionFileIcon.icns")
property ScriptTitle : "Spotlight Meta Data for Items info"

set infoIcon to a reference to file ((path to library folder from system domain as text) & "CoreServices:CoreTypes.bundle:Contents:Resources:AlertNoteIcon.icns")

set idOfFrontApp to getfrontAppId()
set ReportText to ""
set failed to false
try
	
	tell application "Finder"
		activate
		set selected_items_list to (get selection) as alias list
		
		set selCount to count selected_items_list
		
		if selCount ñ‰  0 then set last_item to the last item of the selected_items_list as text
		
		try
			set startPath to target of its Finder window 1 as alias
		on error
			set starpath to path to desktop folder
		end try
		
	end tell
	
	
	if selCount = 0 then
		tell application "SystemUIServer"
			activate
			try
				set the selected_items_list to (choose file with prompt "Choose the files or folders you want to see info for for" default location startPath with multiple selections allowed)
			on error
				set failed to true
			end try
		end tell
		
		set selCount to count selected_items_list
		if ((count of the selected_items_list) = 0) or failed then
			alertDialog({aTextMessage:"No files or applications are selected.", aTextTitle:my ScriptTitle, timeInSecs:300, btnAsList:{"Ok"}, iconAsFileRef:infoIcon, bundleIdOfFrontApp:idOfFrontApp})
			abortNicely({bundleIdFrontApp:idOfFrontApp})
		end if
		set last_item to the last item of the selected_items_list as text
		
	end if
	
	set failed to false
	tell application "SystemUIServer"
		activate
		try
			set outputType to button returned of (display dialog "Please choose form of output" with title ScriptTitle buttons {"Cancel", "Report", "Dialogs"} cancel button 1 default button 3 with icon genericIcon)
		on error
			set failed to true
		end try
	end tell
	if failed then abortNicely({bundleIdFrontApp:idOfFrontApp})
	
	repeat with this_item in the selected_items_list
		if outputType is "Dialogs" then
			set ReportText to ""
		end if
		tell application "Finder" to set item_name to name of this_item
		
		set filepath to quoted form of POSIX path of (this_item as alias)
		set metaRec to metaDataRecord for filepath
		set metaList to Rec2UserKeyValues(metaRec)
		if outputType is "Report" then
			set ReportText to ReportText & return & "==================================" & return & "Name: " & item_name & return
		else
			set ReportText to ReportText & return & "Name: " & item_name & return
		end if
		
		repeat with i from 1 to (count metaList)
			
			set ReportText to ReportText & item 1 of item i of my metaList & ": "
			if class of item 2 of item i of my metaList = list then
				set oltids to AppleScript's text item delimiters
				set AppleScript's text item delimiters to "\" , \""
				set tmpTxt to item 2 of item i of my metaList as text
				set AppleScript's text item delimiters to oltids
				set ReportText to ReportText & "{ \"" & tmpTxt & "\" }" & return
			else
				set ReportText to ReportText & item 2 of item i of my metaList & return
			end if
		end repeat
		
		
		if outputType is "Dialogs" then
			set the clipboard to ReportText
			if contents of this_item is the last_item or selCount is 1 then
				set the button_list to {"Done"}
			else
				set the button_list to {"Cancel", "Next"}
			end if
			-- display the information
			with timeout of 900 seconds
				tell application "SystemUIServer"
					activate
					
					display dialog ReportText with title ScriptTitle buttons the button_list default button (the last item of the button_list)
				end tell
			end timeout
		end if
	end repeat
	if outputType is "Report" then
		
		tell application "TextEdit"
			activate
			make new document at the front
			set text of front document to "F i l e   i n f o r m a t i o n " & return & ReportText
		end tell
	end if
on error e number N
	if not failed then
		alertDialog({aTextMessage:e & " " & N, aTextTitle:my ScriptTitle, timeInSecs:300, btnAsList:{"Ok"}, iconAsFileRef:infoIcon, bundleIdOfFrontApp:idOfFrontApp})
		abortNicely({bundleIdFrontApp:idOfFrontApp})
	end if
end try


on metaDataRecord for fp
	-- http://macscripter.net/edit.php?id=154143 NG
	-- Ensure that we have a quoted POSIX path to the file.
	if (fp's class is text) then
		if (fp does not start with "'/") then
			if (fp does not start with "/") then set fp to POSIX path of fp
			set fp to quoted form of fp
		end if
	else
		set fp to quoted form of POSIX path of (fp as alias)
	end if
	
	-- Get the metadata text and edit it almost to compilability. Mark date entries with unlikely tags.  ;)
	set rs to do shell script "mdls " & fp & " | sed -Ee 's| *=|:|' -e 's|^ +([^\"][a-zA-Z]*[^\"])$|\"\\1\"|' -e 's|\"?([0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} [+-][0-9]{4})\"?|<McUsr>\\1</McUsr>|' -e 's|\\($|{ ÂŹ|' -e 's|^\\)|}|' -e 's|,$|, ÂŹ|' -e 's|([^ÂŹ])$|\\1, ÂŹ|'"
	
	if ((count rs) > 0) then
		-- Append braces for the record representation.
		set rs to "{" & text 1 thru -4 of rs & "}"
		
		set astid to AppleScript's text item delimiters
		-- Zap any stray commas at the ends of lists.
		set AppleScript's text item delimiters to ", ÂŹ" & return & "}"
		set rs to rs's text items
		set AppleScript's text item delimiters to " ÂŹ" & return & "}"
		set rs to rs as text
		-- Replace the ISO dates with AppleScript dates transposed to the computer's time zone and coerced to text as per the local preferences. Requires AS 2.1 (Snow Leopard) or later for the multiple TIDs.
		set AppleScript's text item delimiters to {"<McUsr>", "</McUsr>"}
		set rs to rs's text items
		repeat with i from 2 to (count rs) by 2
			set dateString to item i of rs
			set item i of rs to "date \"" & getASLocalDate(dateString) & "\""
		end repeat
		set AppleScript's text item delimiters to ""
		set rs to rs as text
		set AppleScript's text item delimiters to astid
		
		-- Return the "compiled" record.
		return (run script rs)
	else
		return {}
	end if
end metaDataRecord

-- Return a local AppleScript date from a given ISO date/time with time-zone displacement.
on getASLocalDate(ISODate)
	-- Get the ISO date/time as as AppleScript date object. (The date object will be reused for different purposes below to save multiple calls to 'current date'.)
	tell (current date) to set {day, {year, its month, day, its hours, its minutes, its seconds}, ASDate} to {1, words 1 thru 6 of ISODate, it}
	
	-- Subtract the time-zone displacement to transpose to GMT.
	set astid to AppleScript's text item delimiters
	set AppleScript's text item delimiters to space
	tell (text item -1 of ISODate) as integer to set ASGMTDate to ASDate - (it div 100 * hours + it mod 100 * minutes)
	set AppleScript's text item delimiters to astid
	
	-- Subtract the Unix era start from the GMT date to get the number of seconds since then.
	tell ASDate to set {day, month, year, time} to {1, 1, 1970, 0}
	set eraTime to ASGMTDate - ASDate
	-- Coerce this figure to text in straight decimal notation.
	if (eraTime > 99999999) then
		set eraTime to (eraTime div 100000000 as text) & text 2 thru 9 of (100000000 + eraTime mod 100000000 as integer as text)
	else if (eraTime < -99999999) then
		set eraTime to (eraTime div 100000000 as text) & text 3 thru 10 of (-100000000 + eraTime mod 100000000 as integer as text)
	else
		set eraTime to eraTime as text
	end if
	
	-- Run the figure through "date" to get the corresponding machine-local date/time and convert to AS.
	do shell script ("date -r " & eraTime & " '+%Y %m %d %H %M %S'")
	tell ASDate to set {year, its month, day, its hours, its minutes, its seconds} to words of result
	
	return ASDate
end getASLocalDate

on getfrontAppId() -- Returns bundleid of active app
	local frontappId
	set frontappId to ""
	tell application "System Events"
		set frontappId to bundle identifier of first application process whose frontmost is true
	end tell
	return frontappId
end getfrontAppId

on abortNicely(R) -- Returns Nothing
	
	tell application "System Events" to tell application process id (bundleIdFrontApp of R)
		key down control
		key code 118
		key up control
	end tell
	error number -128
end abortNicely

on Rec2UserKeyValues(recAny)
	-- http://macscripter.net/viewtopic.php?id=36842 yiam-jin-qui
	-- USE THE CLIPBOARD TO MAKE THE RECORD KEYS LEGIBLE
	set the clipboard to recAny
	set recLegible to (the clipboard as record)
	set lngPairs to count of (recAny as list)
	if lngPairs < 1 then return {}
	
	-- COLLECT ANY USER-DEFINED KEY-VALUE PAIRS
	set lstKeyValue to {}
	try
		set lstUser to list of recLegible
	on error
		display dialog (do shell script "osascript -e 'the clipboard as record'") buttons "OK" default button 1 with title "Contents of record"
		return {}
	end try
	
	repeat with i from 1 to (length of lstUser) - 1 by 2
		set end of lstKeyValue to {item i of lstUser, item (i + 1) of lstUser}
	end repeat
	
	-- IF ANY PAIRS ARE MISSING, TRY SOME SYSTEM-DEFINED KEYNAMES
	if (count of lstKeyValue) < lngPairs then
		try
			set beginning of lstKeyValue to {"Date", date of recAny}
		end try
		try
			set beginning of lstKeyValue to {"Name", name of recAny}
		end try
	end if
	lstKeyValue
end Rec2UserKeyValues

on alertDialog(R) -- Returns Nothing
	-- R : {aTextMessage:theMessage,aTextTitle:thetitle,timeInSecs:lenToTimeout,btnAsList:theButton,iconAsFileRef:theIcon,bundleIdOfFrontApp:frontappId}
	local res, failed, e, N
	set failed to false
	tell application "SystemUIServer"
		activate
		try
			if (iconAsFileRef of R) is null then
				set res to button returned of (display dialog (aTextMessage of R) with title (aTextTitle of R) giving up after (timeInSecs of R) buttons (btnAsList of R) default button 1)
			else
				set res to button returned of (display dialog (aTextMessage of R) with title (aTextTitle of R) giving up after (timeInSecs of R) buttons (btnAsList of R) default button 1 with icon (iconAsFileRef of R))
			end if
			if res = "" then set failed to true
		on error e number N
			set failed to true
			
		end try
	end tell
	if failed is true then
		abortNicely({bundleIdFrontApp:(bundleIdOfFrontApp of R)}) -- Returns Nothing
	end if
	return
end alertDialog

Thanks, the problem is that dates on your machines aren’t quoted and they are on my machine. So even the quotations are different between localizations. On my machine if a value contains a space the field is quoted (like normal shell values). Again thanks, now I can fix it later to make it more universal today.

Hello mouramartins!

I agree with your solution for representing the date in a record, if all the run script constructs are to be avoid it.
I can find NO OTHER WAY than yours to represent the date. Oh well, I might add a hard space ascii character 202 in front of the datestring but that just leads to other problems. :slight_smile:

Once you have your date representation, it is really no big problem, converting it back to the “TZ-form” should that be wished form. That this should be so tricky, was nothing I had foreseen. And well, here is the handler I made from yours. I’ll not use it for the moment, but it may come in handy the second I am interested in reading metadata properties from files!

Thanks!

I’ll go optimize the list version for now, as said, and that is the one I am going to use for now.


on metaDataRecord for fp
	--	Executed when files are dropped on the script
	local rs, prs, k, islist, p, x, i, isdate
	
	set rs to (do shell script "mdls " & fp)
	set prs to "{"
	set islist to false
	set isdate to false
	(*
		eg:
		kMDItemUsedDates               = (
	    2012-08-09 00:00:00 +0100
		)
*)
	repeat with k from 1 to count of paragraphs of rs
		set p to paragraph k of rs
		if islist then
			if text 1 of p is ")" then
				set islist to false
				set x to x & "},"
				set prs to prs & x
				set isdate to false
			else
				if isdate then
					set x to x & "date \"" & getdate(p) & "\""
					if last character of p = "," then
						set x to x & ","
					end if
				else
					if ((offset of "\"" in p) = 0) then set p to "\"" & p & "\"" ” copes with Spotlight "constants"
					set x to x & p
				end if
			end if
		else
			set i to offset of "=" in p
			if i > 1 then
				set x to (text 1 thru (i - 1) of p) & ":"
				if text (i + 2) of p = "(" then
					set x to x & "{"
					set islist to true
					if x contains "date" then
						set isdate to true
					end if
					
				else
					if x contains "date" then
						set x to x & "date \"" & getdate(text (i + 2) thru -1 of p) & "\""
					else
						set x to x & text (i + 2) thru -1 of p
					end if
					set prs to prs & x & ","
				end if
			end if
			
		end if
	end repeat
	set prs to text 1 thru -2 of prs & "}"
	set prs to (run script prs)
	return prs
	
end metaDataRecord

on getdate(t)
	--	    2012-08-09 00:00:00 +0100
	set d to every word of t
	set r to current date
	set year of r to item 1 of d
	set month of r to item 2 of d
	set day of r to item 3 of d
	set time of r to ((item 4 of d) * hours + (item 5 of d) * minutes + (item 6 of d))
	
	-- for the sake of completeness, can be omited
	set tz to 1 * (item 8 of d)
	if item 7 of d = "-" then set tz to -tz
	set tz to hours * (tz div 100) + minutes * tz mod 100
	set r to r + (time to GMT) - tz
	-- end of completeness sake
	
	return (short date string of r & " " & time string of r)
	-- portuguese for example flips with date as text
end getdate

Hello!

I tried to optimize my solution for the list, but it was in fact good enough as it was.

I instrumented your handler and my handler, yours used 92 ticks (millisecs) mine used 123. I find that acceptable, that the list version uses 20% more time. Considering I don’t have to parse the record back to a list again.

Maybe I could optimize it a little using text item delimiters, but I think it won’t help much really, and defers that test.

Hello!

This is some interesting docs as well:

I found this here.

The share reason for me for making this in the first place, was to have something to look at while playing with mdfind.

The usage of this, is of course to use it in mdfind -interpret queries, what you really can do with spotlight is described here

While we’re talking about [b]mdfind]/b] [ur=http://macscripter.net/viewtopic.php?id=24765l]this article by Adam Bell is a fine one[/url]
This from O’reilly is also good.

Hello!

I don’t like it, when I can’t make it, so I made one final effort, using a quoted string. For making a general generic solution, relying on a record.

It works perfectly well, I also found a little bug, for those arcane cases, when mdls returns a constant for something.

That went wrong during coercion to a record, so I added a little test to see if list items are quoted, so now it works for those cases as well. :cool:

I’m also changing the handlers for my own private usage to get their mdls data sent as a parameter in, as I then feel the routines will be more flexible.


on metaDataRecord for fp
	--  mouramartins Macscripter.net http://macscripter.net/viewtopic.php?id=39249
	local rs, prs, k, islist, p, x, i, isdate
	
	set rs to (do shell script "mdls " & fp)
	set prs to "{"
	set islist to false
	set isdate to false
	(*
		eg:
		kMDItemUsedDates               = (
	    2012-08-09 00:00:00 +0100
		)
*)
	repeat with k from 1 to count of paragraphs of rs
		set p to paragraph k of rs
		if islist then
			if text 1 of p is ")" then
				set islist to false
				set x to x & "},"
				set prs to prs & x
				set isdate to false
			else
				if isdate then
					set x to x & "\"" & quoted form of (p) & "\""
					if last character of p = "," then
						set x to x & ","
					end if
				else
					if ((offset of "\"" in p) = 0) then set p to "\"" & p & "\""
					set x to x & p
				end if
			end if
		else
			set i to offset of "=" in p
			if i > 1 then
				set x to (text 1 thru (i - 1) of p) & ":"
				if text (i + 2) of p = "(" then
					set x to x & "{"
					set islist to true
					if x contains "date" then
						set isdate to true
					end if
					
				else
					if x contains "date" then
						set x to x & "\"" & quoted form of (text (i + 2) thru -1 of p) & "\""
					else
						set x to x & text (i + 2) thru -1 of p
					end if
					set prs to prs & x & ","
				end if
			end if
			
		end if
	end repeat
	set prs to text 1 thru -2 of prs & "}"
	set prs to (run script prs)
	return prs
	
end metaDataRecord


:frowning:

It works, on the initial testfile and no others! So, the version getting dates out, still stands, and I move over to something more productive.

I have really tried to figure out what is going wrong now, in the creation of the record, but I can’t for the bare life of me think of anything else, thant it is an even/odd thing, when coercing the text into a record. The culprit this time, being the code added to handle the “constants” from mdls.

Edit

It is indeed the date thing, that makes it malfunction. So I stick with the original version in post #14 updated to handle Spotlight “constants”.

As for timing:

The routine that delivers a record, and uses the other handle to get at the properties uses 93 ticks.
The routine that delivers a list up front uses 106 ticks.

There aren’t more than ca. 25 Spotlight attributes, and small lists in this metadata, so the diferences will grow, the routine delivering the record being the better!

Nice catch!

In those cases, in lists, the script relies on the comma supplied by mdls:

Simply adding quotes results in
“Regular,”

not the expected “Regular”,

Pizza Hut is going to make a fortune on us!

Na! Off to a barbecue in the garden I go!

As I understood it dates are not quoted in the same way that numbers are not quoted.

That one word results are not quoted is a valuable insight! Thanks.