Using EXIF Date on Photos

I would love to retrieve the EXIF data from an image to use in the file name. Is there any way to easily retrieve that data using Keyboard Maestro?

If you do a google on "AppleScript EXIF" you should find a number of solutions.

I don't know if KM has a native solution, but you could setup batch processing of files with KM, and call an AppleScript.

Hey Gary,

Not specifically no, however there is a great command-line tool for this:

Using that in combination with Keyboard Maestro makes such a thing a cinch.

Basic usage is:

exiftool "/posix path to file/the file.jpg"

Help is available on the site and via the Terminal using the command: exiftool -h

-Chris

And you could also do something with an Execute JavaScript action in KM.

Here are examples of:

  • getting a particular EXIF value like 'DateTimeOriginal'
  • Listing any EXIF value types (value keys) that are available in an image
  • Getting all of the EXIF key:value pairs in an image

((function () {
    'use strict';

    // exifValue :: String -> String -> String
    function exifValue(strPath, strKey) {
        var maybeData = exifData(strPath) || {};

        return maybeData[strKey];
    }

    // exifKeys :: String -> [String]
    function exifKeys(strPath) {
        var dctData = exifData(strPath) || {};

        return Object.keys(
                dctData
            )
            .sort();
    }

    // exifData :: String -> Dictionary
    function exifData(strPath) {
        ObjC.import('AppKit');

        return ObjC.deepUnwrap(
            $.NSBitmapImageRep.imageRepWithContentsOfFile(
                $(strPath)
                .stringByStandardizingPath
            )
            .valueForProperty($.NSImageEXIFData)
        );
    }

    // for type strings see: Apple's 'System-Declared Uniform Type Identifiers'
    // if strType is omitted, files of all types will be selectable
    // filePathChoice :: String -> String -> String
    function filePathChoice(strPrompt, strType) {
        var a = Application.currentApplication();

        return (a.includeStandardAdditions = true, a)
            .chooseFile({
                withPrompt: strPrompt,
                ofType: strType
            })
            .toString();
    }

    var strPath = filePathChoice('Choose image file', 'public.image');

    return strPath && {

        // Getting a particular value from the EXIF
        time: exifValue(strPath, 'DateTimeOriginal'),

        // Listing all the available value types in the EXIF
        sortedKeys: exifKeys(strPath),

        // Getting ALL of the EXIF data into a JavaScript dictionary
        allData: exifData(strPath)
    }
})();
2 Likes

Nice.

I would have done something similar with AppleScriptObjC, except that exiftool eats them both for lunch in terms of information returned.

-Chris

Out of interest, if one uses AppleScriptObjC to harvest the data as a record, what's the best way to get a list of the keys ?

Perhaps some method on NSDictionary ?

UPDATE

Got it, I think – looks like NSDictionary.allKeys

Yep.

-Chris

# Handlers for making a record from separate lists of keys and values,
# and for returning the keys of a record as a flat list.
--------------------------------------------------------------------------------
use framework "Foundation"
--------------------------------------------------------------------------------

set theKeys to {"fname", "lname"}
set theValues to {"Chris", "Stone"}

set theRecord to its recWithLabels:theKeys andValues:theValues

set recKeys to its returnLabelsFromRec:theRecord

--------------------------------------------------------------------------------
--» HANDLERS
--------------------------------------------------------------------------------
on recWithLabels:theKeys andValues:theValues
   return (current application's NSDictionary's dictionaryWithObjects:theValues forKeys:theKeys) as record
end recWithLabels:andValues:
--------------------------------------------------------------------------------
on returnLabelsFromRec:aRecord
   return (current application's NSDictionary's dictionaryWithDictionary:aRecord)'s allKeys() as list
end returnLabelsFromRec:
--------------------------------------------------------------------------------
3 Likes

I am keen to use this as I have hundreds of files where the Creation Date shown in Finder is no longer the same as that in the EXIF data.

Ideally I'd like to be able to iterate through a series of photos in a Folder called "Wrong Date" and update the Creation Date with the EXIF data.

I can build a KM macro that I think would do the trick—except I don't know how how to read across from KM into JXA and vice-versa. ComplexPoint's script asks for a prompt and, as shown below, I would like to read in the filename held in the variable %Photo%:

I'm guessing that this is where it's happening:

function filePathChoice(strPrompt, strType) {
    var a = Application.currentApplication();

    return (a.includeStandardAdditions = true, a)
        .chooseFile({
            withPrompt: strPrompt,
            ofType: strType
        })
        .toString();
}

And presumably rather than have

        .chooseFile({
            withPrompt: strPrompt,

I need some kind of .chooseFile({ defined by this variable})

The next question is getting the EXIF data that the JXA script has pulled and putting that into the KM macro, where I have some RegEx waiting to do the next piece of work:

Thanks for any help.

Hey @Rather,

The exiftool Unix Command-line tool makes this relatively easy to do from the Terminal.

cd "~/test_directory/exif_test/";
exiftool "-filemodifydate<datetimeoriginal" "-modifydate<datetimeoriginal" "-metadatadate<datetimeoriginal" "-historywhen<datetimeoriginal" -overwrite_original *.jpg;

Here's a relevant thread:

http://u88.n24.queensu.ca/exiftool/forum/index.php/topic,3848.0.html?PHPSESSID=qv9denoee6rahde0btt584mvj6

What kind of files are you managing? I've limited the shell script to jpegs, but that is easily altered.

Make sure you test on COPIES before working with the originals.

-Chris

Thank-you @ccstone,
I will take a look at this—and try it out on some duplicate data!