A simple reader for tags in FLAC sound files. The whole script will read the tags and produce a text file and the embedded images, as files, next to the original file, as an example usage.
The main code starts with readtags(afile) and will return a record with the found tags:
{comments:{}, picts:{}}
comments comes as a list of records: {tag: “a tag”, value: “the value”}
picts comes as a list of records: {type:pictype (textual form), mimetype:mimetype, description: the pict description, width:x, height:y, depth:z, indexedColors:n, thePict: as JPEG picture or GIF picture or PostScript picture or data}
Contrarily to most tag readers this script will return a list of values, if there is one, including the pictures, as defined in the specifications.
And now a word from our sponsors:
To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer’s Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the “Waiver”). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer’s heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer’s express Statement of Purpose.
The above text has a patent pending.
property blocktypes : {"STREAMINFO", "PADDING", "APPLICATION", "SEEKTABLE", "VORBIS_COMMENT", "CUESHEET", "PICTURE", "reserved", "invalid"}
property pictTypes : {"Other", "32x32 pixels 'file icon' (PNG only)", "Other file icon", "Cover (front)", "Cover (back)", "Leaflet page", "Media (e.g. label side of CD)", "Lead artist/lead performer/soloist", "Artist/performer", "Conductor", "Band/Orchestra", "Composer", "Lyricist/text writer", "Recording Location", "During recording", "During performance", "Movie/video screen capture", "A bright coloured fish", "Illustration", "Band/artist logotype", "Publisher/Studio logotype"}
property standardcomments : {"vendor_string", "TITLE", "VERSION", "ALBUM", "TRACKNUMBER", "ARTIST", "PERFORMER", "COPYRIGHT", "LICENSE", "ORGANIZATION", "DESCRIPTION", "GENRE", "DATE", "LOCATION", "CONTACT", "ISRC", "DISCNUMBER", "REPLAYGAIN_TRACK_GAIN", "REPLAYGAIN_TRACK_PEAK", "REPLAYGAIN_ALBUM_GAIN", "REPLAYGAIN_ALBUM_PEAK", "HASH", "ALBUMARTIST", "COMPILATION"}
property tags : {comments:{}, picts:{}}
on open of theFiles
-- Executed when files are dropped on the script
repeat with f in theFiles
if readTags(f) is not missing value then
writeTags(f)
end if
end repeat
end open
-----------------------
on readTags(fi)
set f to open for access fi
try
set tags to {comments:{}, picts:{}}
if (read f for 4) = "fLaC" then
set lastblock to false
repeat while not lastblock
set blok to readBlockHead(f)
set lastblock to lastblock of blok
if bloktype of blok = 127 then
error "invalid bloc in file " & fi as text
end if
set blocksize to bloklen of blok
set blocktype to bloktype of blok
if blocktype = "STREAMINFO" then
read f for blocksize
else if blocktype = "PADDING" then
read f for blocksize
else if blocktype = "APPLICATION" then
read f for blocksize
else if blocktype = "SEEKTABLE" then
read f for blocksize
else if blocktype = "VORBIS_COMMENT" then
copy parsecomm(f, blocksize) to comments of tags
else if blocktype = "CUESHEET" then
read f for blocksize
else if blocktype = "PICTURE" then
copy readPic(f) to end of picts of tags
else if blocktype = "reserved" then
read f for blocksize
end if
end repeat
else
close access f
return missing value
end if
end try
close access f
return tags
end readTags
on read32bit(f)
-- When reading binary data, read always uses big-endian byte order.
set n to (read f for 4 as unsigned integer)
--swap the whole shebang!
set r to ((n mod 256) as integer) * 16777216 --256 * 256 * 256
set n to n div 256
set r to r + (n mod 256) * 65536 --256 * 256
set n to n div 256
set r to r + (n mod 256) * 256
set n to n div 256
set r to r + (n mod 256)
end read32bit
on readBlockHead(f)
set blocktypes to {"STREAMINFO", "PADDING", "APPLICATION", "SEEKTABLE", "VORBIS_COMMENT", "CUESHEET", "PICTURE"}
set n to read f for 4 as unsigned integer
set btype to n div (16777216) --256 * 256 * 256
set blen to (n mod (16777216)) as integer
set lastblock to (btype > 127)
set btype to btype mod 128
if btype < 127 then
if btype > 6 then
set btype to "reserved"
else
set btype to item (btype + 1) of blocktypes
end if
end if
return {bloktype:btype, bloklen:blen, lastblock:lastblock}
end readBlockHead
on readPic(f)
set pictTypes to {"Other", "32x32 pixels 'file icon' (PNG only)", "Other file icon", "Cover (front)", "Cover (back)", "Leaflet page", "Media (e.g. label side of CD)", "Lead artist/lead performer/soloist", "Artist/performer", "Conductor", "Band/Orchestra", "Composer", "Lyricist/text writer", "Recording Location", "During recording", "During performance", "Movie/video screen capture", "A bright coloured fish", "Illustration", "Band/artist logotype", "Publisher/Studio logotype"}
set pictype to read f for 4 as unsigned integer
if pictype < 21 then
set pictype to item (pictype + 1) of pictTypes
end if
set mimetype to read f for 4 as unsigned integer
if mimetype > 0 then
-- even if mandatory
set mimetype to read f for mimetype
else
set mimetype to ""
end if
set picdescr to read f for 4 as unsigned integer
if picdescr > 0 then
set picdescr to read f for picdescr
else
set picdescr to ""
end if
set x to read f for 4 as unsigned integer
set y to read f for 4 as unsigned integer
set z to read f for 4 as unsigned integer
set n to read f for 4 as unsigned integer
set pic to read f for 4 as unsigned integer
if mimetype = "image/jpeg" then
set pic to read f for pic as JPEG picture
else if mimetype = "image/gif" then
set pic to read f for pic as GIF picture
else if mimetype = "application/pdf" then
set pic to read f for pic as PostScript picture
else
set pic to read f for pic as data
end if
--read f for blocksize as picture
--read f for blocksize as GIF picture
return {type:pictype, mimetype:mimetype, description:picdescr, width:x, height:y, depth:z, indexedColors:n, thePict:pic}
end readPic
on parsecomm(f, n)
set r to {}
set comments to {}
(*
1) [vendor_length] = read an unsigned integer of 32 bits
2) [vendor_string] = read a UTF-8 vector as [vendor_length] octets
3) [user_comment_list_length] = read an unsigned integer of 32 bits
4) iterate [user_comment_list_length] times {
5) [length] = read an unsigned integer of 32 bits
6) this iteration's user comment = read a UTF-8 vector as [length] octets
*)
try
set vendor_length to read32bit(f) -- little endian
set comments to {{tag:"vendor_string", value:read f for vendor_length as «class utf8»}}
set list_length to read32bit(f)
repeat list_length times
set l to read32bit(f)
set acomm to read f for l as «class utf8»
set i to offset of "=" in acomm
if i = 0 then
set tag to acomm
set value to "ERROR"
else if i = length of acomm then
set value to ""
set tag to text 1 thru -2 of acomm
else
set tag to text 1 thru (i - 1) of acomm
set value to text (i + 1) thru -1 of acomm
end if
set ac to {tag:tag, value:value}
set found to false
repeat with i in comments
if {tag:tag} is in i then
set found to true
copy {value} & value of i to value of i
exit repeat
end if
end repeat
if not found then
copy ac to end of comments
end if
end repeat
on error e
display alert e
end try
return comments
end parsecomm
-------------
on writeTags(fi)
set {astid, AppleScript's text item delimiters} to {AppleScript's text item delimiters, {" "}} -- \tab
set pth to (POSIX path of fi) & ".txt"
set f to open for access (pth as POSIX file) with write permission
set eof f to 0
repeat with c in comments of tags
-- tag \tab value(s)
write tag of c & " " & value of c & "
" to f
end repeat
set AppleScript's text item delimiters to astid
set i to 1
repeat with p in picts of tags
write type of p & " " & mimetype of p & " " & POSIX path of fi & "." & i & ".jpg
," to f
set g to open for access POSIX file (POSIX path of fi & "." & i & ".jpg") with write permission
set eof g to 0
write thePict of p to g
close access g
set i to i + 1
end repeat
close access f
end writeTags