Reading, Deleting, and Writing ID3 tags on MP3 Files
Hello all,
I’d like to expand on the following excellent post:
https://macscripter.net/viewtopic.php?id=45629
Edit. I found this post after searching for “initWithURL:”
https://www.macscripter.net/viewtopic.php?id=46543
I honestly did not think of doing that search until 24 hours after I made this post. Sorry for the oversight on my part. Now I will search some more for the editing of the NSMetadataItem.
Instead of moving the files I’d like to just read the tags that the current file has (partially pieced together below), delete ALL metadata from the file, and then write new metadata back to the file.
Here is what I have chopped the script above down to, in it’s “complete” form.
use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
set thisFile to choose file of type {"mp3"}
set |⌘| to current application
set newArray to |⌘|'s NSMutableArray's arrayWithArray:{}
set thePath to (|⌘|'s NSString's stringWithString:(POSIX path of thisFile))
set theExtension to thePath's pathExtension()
set theName to thePath's lastPathComponent()
set theContainer to thePath's stringByDeletingLastPathComponent()
set thePred to |⌘|'s NSPredicate's predicateWithFormat_("kMDItemFSName == %@", theName)
-- build and start query
set theQuery to |⌘|'s NSMetadataQuery's new()
(theQuery's setPredicate:thePred)
(theQuery's setSearchScopes:{theContainer})
theQuery's startQuery()
-- loop until it's stopped gathering
repeat while theQuery's isGathering() as boolean
delay 0.1
end repeat
-- stop and get the first NSMetadataItem (there can only be one in this case)
theQuery's stopQuery()
try
set metaItem to (theQuery's resultAtIndex:0)
-- get the required attribute values, or default alternatives where the attributes weren't found.
-- Firstly the artist ("authors") and genre values.
set theArtist to (metaItem's valueForAttribute:(|⌘|'s NSMetadataItemAuthorsKey))
set moveItArtist to (theArtist is not missing value)
if (theArtist is missing value) then
set theArtist to "(no artist)"
else
set theArtist to (theArtist's componentsJoinedByString:",")
set theArtist to my decodeText(theArtist)
end if
set theGenre to (metaItem's valueForAttribute:(|⌘|'s NSMetadataItemMusicalGenreKey))
set moveItGenre to (theGenre is not missing value)
if (theGenre is missing value) then set theGenre to "(no genre)"
-- Get the key, tempo, and title values.
set theKey to (metaItem's valueForAttribute:(|⌘|'s NSMetadataItemKeySignatureKey))
set moveItKey to (theKey is not missing value)
if (theKey is missing value) then set theKey to "(no key signature)"
set theTempo to (metaItem's valueForAttribute:(|⌘|'s NSMetadataItemTempoKey))
set moveItTempo to (theTempo is not missing value)
if (theTempo is missing value) then set theKey to "(no tempo)"
end try
on listFolder:POSIXPath # handler using ASObjC ADDED
set fileManager to current application's NSFileManager's defaultManager()
set aURL to current application's |NSURL|'s fileURLWithPath:POSIXPath
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 theFiles to theEnumerator's allObjects()
set theFiles to theFiles's filteredArrayUsingPredicate:(current application's NSPredicate's predicateWithFormat:"(lastPathComponent ENDSWITH '.mp3') OR (lastPathComponent ENDSWITH '.aiff')")
return theFiles as list # if we work upon files
--return (theFiles's valueForKey:"path") as list # if we work upon POSIX paths
end listFolder:
on decodeText(theText) # Handler written by Nigel GARVEY
set |⌘| to current application
--set theText to |⌘|'s class "NSString"'s stringWithString:(theText)
-- If the string contains at least two consecutive 8+bit characters, assume it's a mangled result.
if ((theText's rangeOfString:("[\\u0080-\\U0010ffff]{2}") options:(|⌘|'s NSRegularExpressionSearch))'s |length|() > 0) then
set dataObj to (theText's dataUsingEncoding:(|⌘|'s NSISOLatin1StringEncoding))
set theText to (|⌘|'s class "NSString"'s alloc()'s initWithData:(dataObj) encoding:(|⌘|'s NSUTF8StringEncoding))
end if
return theText as text
end decodeText
Going through the script, if I am reading things correctly, the following code gets all of the metadata for the file:
set theQuery to |⌘|'s NSMetadataQuery's new()
(theQuery's setPredicate:thePred)
(theQuery's setSearchScopes:{theContainer})
theQuery's startQuery()
-- loop until it's stopped gathering
repeat while theQuery's isGathering() as boolean
delay 0.1
end repeat
-- stop and get the first NSMetadataItem (there can only be one in this case)
theQuery's stopQuery()
And then this code takes the metadata from the query above and then pulls out the values for 4 specific tags:
set metaItem to (theQuery's resultAtIndex:0)
-- get the required attribute values, or default alternatives where the attributes weren't found.
-- Firstly the artist ("authors") and genre values.
set theArtist to (metaItem's valueForAttribute:(|⌘|'s NSMetadataItemAuthorsKey))
set moveItArtist to (theArtist is not missing value)
if (theArtist is missing value) then
set theArtist to "(no artist)"
else
set theArtist to (theArtist's componentsJoinedByString:",")
set theArtist to my decodeText(theArtist)
end if
set theGenre to (metaItem's valueForAttribute:(|⌘|'s NSMetadataItemMusicalGenreKey))
set moveItGenre to (theGenre is not missing value)
if (theGenre is missing value) then set theGenre to "(no genre)"
-- Get the key, tempo, and title values.
set theKey to (metaItem's valueForAttribute:(|⌘|'s NSMetadataItemKeySignatureKey))
set moveItKey to (theKey is not missing value)
if (theKey is missing value) then set theKey to "(no key signature)"
set theTempo to (metaItem's valueForAttribute:(|⌘|'s NSMetadataItemTempoKey))
set moveItTempo to (theTempo is not missing value)
if (theTempo is missing value) then set theKey to "(no tempo)"
solved the reading of all tags. See post referenced at the top of this post.
When done reading the attributes, how would I go about zeroing out all of those attributes?
Reading the developer documentation for NSMetadataItem it looks like you would use
initWithURL:
And I am guessing you would use the same command to write new attribute values back to the file. Is the URL a path back to a document that has the attribute:value pairs?
Edited for clarity (I hope).