Extract exif data solution brainstorming

So I’m in the process of migrating image storage over the Cloudflare images. Initial setup isn’t particularly elegant, but I can’t contain client review files in a git repo style deployment.

Anyways…so basically I’m stripping all the large image assets out and sending them to get served.

I was getting real spoiled extracting image exif data with hugo processing though. And I plan to store my original image export in the page bundle and not commit them, life is just easier if I store it there.

Before I beat my head into a wall trying to figure this out…

Idea 1
Would it even be possible to process the image locally within hugo, and get the exif data, somehow save that json output file since the git deployment wont have the file to process on the server?

Idea 2
Ignore the image, I’m in the window, run some sort of exif tool script to output the formatted exif data for a json file or just to paste into the front matter?

Idea #2 sounds about right.

You could use Phil Harvey’s ExifTool to create a JSON file in your project’s data directory prior to each build. For example:

exiftool -recurse -json content/ > data/exif.json

data/exif.json

[{
  "SourceFile": "content/post/post-1/a.jpg",
  "FileName": "a.jpg",
  "Directory": "content/post/post-1",
  "FileSize": "18 kB",
  "ImageDescription": "Image A",
  "ImageWidth": 600,
  "ImageHeight": 400,
  ...
},
{
  "SourceFile": "content/post/post-1/b.jpg",
  "FileName": "b.jpg",
  "Directory": "content/post/post-1",
  "FileSize": "155 kB",
  "ImageDescription": "Image B",
  "ImageWidth": 600,
  "ImageHeight": 400,
  ...
}]
1 Like

Thanks. Exiftool was what I intended to refer to. I’ve used it for years, but for really surface level stuff.

I’ll take a crack at this tonight, see if I can get it working like how I see it in my head.

Does it have to be in the data directory? or can it live in a page bundle?

Data files can live in a page bundle, to be accessed as a page resource.

But in your scenario, it is much simpler to create a single data file using the one-liner from my previous comment.

To create multiple data files (one for each page bundle) you will need to write a script that runs exiftool recursively in each page bundle. That seems like a lot of work for a limited return.

Still chewing on this one.

I kind of imagined it more like I run hugo new photos/2022-06-24-post-title/ and the post bundle builds, I drop the image into that (I’m not checking the jpg into git, so I can’t process anything from the photo on build), from there I run terminal from that director, process some form of exif read on the photograph, extracts the json file and saves it to the root of the post-bundle.

The json is then processed on build to input the correct data in the page to accompany the photograph, which lives in cloudflare images and has the ID matching the filename, assumed I’d upload from the CLI with wrangler to assign the custom ID. I never have the leave the IDE at least.

The json lives with the post/content if I ever move things around in a dramatic fashion.

One data file works too. Just isn’t how I wanted to intuitively create a solution.

Workflow:

  1. Create leaf bundle

    hugo new content/photos/2022-06-24-post-title
    
  2. Copy JPEG images to leaf bundle

  3. Create data file

    cd content/photos/2022-06-24-post-title
    exiftool -json *.jpg > exif.json
    

Resulting structure:

content/
├── photos/
│   └── 2022-06-24-post-1/
│       ├── a.jpg
│       ├── b.jpg
│       ├── exif.json
│       └── index.md
└── _index.md
exif.json
[{
  "SourceFile": "a.jpg",
  "ExifToolVersion": 11.88,
  "FileName": "a.jpg",
  "Directory": ".",
  "FileSize": "17 kB",
  "FileModifyDate": "2022:06:24 10:48:13-07:00",
  "FileAccessDate": "2022:06:24 10:51:09-07:00",
  "FileInodeChangeDate": "2022:06:24 10:48:13-07:00",
  "FilePermissions": "rw-------",
  "FileType": "JPEG",
  "FileTypeExtension": "jpg",
  "MIMEType": "image/jpeg",
  "ExifByteOrder": "Big-endian (Motorola, MM)",
  "ImageDescription": "A kitten",
  "XResolution": 72,
  "YResolution": 72,
  "ResolutionUnit": "inches",
  "YCbCrPositioning": "Centered",
  "ImageWidth": 600,
  "ImageHeight": 400,
  "EncodingProcess": "Baseline DCT, Huffman coding",
  "BitsPerSample": 8,
  "ColorComponents": 3,
  "YCbCrSubSampling": "YCbCr4:2:0 (2 2)",
  "ImageSize": "600x400",
  "Megapixels": 0.240
},
{
  "SourceFile": "b.jpg",
  "ExifToolVersion": 11.88,
  "FileName": "b.jpg",
  "Directory": ".",
  "FileSize": "16 kB",
  "FileModifyDate": "2022:06:24 10:48:13-07:00",
  "FileAccessDate": "2022:06:24 10:51:09-07:00",
  "FileInodeChangeDate": "2022:06:24 10:48:13-07:00",
  "FilePermissions": "rw-------",
  "FileType": "JPEG",
  "FileTypeExtension": "jpg",
  "MIMEType": "image/jpeg",
  "ExifByteOrder": "Big-endian (Motorola, MM)",
  "ImageDescription": "Another kitten",
  "XResolution": 72,
  "YResolution": 72,
  "ResolutionUnit": "inches",
  "YCbCrPositioning": "Centered",
  "ImageWidth": 600,
  "ImageHeight": 400,
  "EncodingProcess": "Baseline DCT, Huffman coding",
  "BitsPerSample": 8,
  "ColorComponents": 3,
  "YCbCrSubSampling": "YCbCr4:2:0 (2 2)",
  "ImageSize": "600x400",
  "Megapixels": 0.240
}]

layouts/photos/single.html
{{ $exif := dict }}
{{ with .Resources.Get "exif.json" }}
  {{ $exif = . | transform.Unmarshal }}
{{ end }}

{{ range .Resources.Match "*.jpg" }}
  {{ $imageDescription := "" }}
  {{ $imageSize := "" }}
  {{ with where $exif "SourceFile" "eq" .Name }}
    {{ with index . 0 }}
      {{ $imageDescription = .ImageDescription }}
      {{ $imageSize = .ImageSize }}
    {{ end }}
  {{ end }}
  <figure>
    <img src="{{ .RelPermalink }}" width="{{ .Width }}" height="{{ .Height }}" alt="{{ $imageDescription }}">
      <figcaption>{{ $imageDescription }} ({{ $imageSize }})</figcaption>
  </figure>
{{ end }}

You would, obviously, need to modify the template code to use your Cloudflare images instead of the locally stored image(s).

2 Likes

Okay, that’s pretty close to how i had it in my brain I think. I just fire the image off with curl to cloudflare images, assign the ID as the filename, and then copy / paste that into the front matter.

Not ideal, it’s a few steps. But it’s like 5 seconds of effort.

As always, thanks for the feedback. It’s awesome to be able to bounce the concept off the community and get responses back like yours. Always more than I expect.

3 Likes

Would it make more sense to process the json as a short code within the post when I retrieve the image/filename ID from cloudflare images?

I spent far longer than I would like admit trying to get this to work. But I fear my programming experience just isn’t far enough along to not have to ask for help.

I grasp the everything up to the single.html template portion, where I need to make the major modifications.

        {{ with .Resources.Get "exif.json" }}
        <section>
            {{ .Content }}
        </section>
        {{ end }}

That currently returns all of the json data. So at least I know that is working. I’ll keep tinkering and see if I can formulate a question that makes sense. You example has been a huge help thus far.