Renaming pictures with Elixir and Mix.install

Every time I come back from the holidays I have a massive amount of media files taken with different devices. Those pictures and videos are named by the devices themselves (IMG_1234.jpg for example) but as an Apple Photos user I never cared about that.

This time though, in addition to storing my collection on the cloud, I also wanted to store pictures and videos on an offline disk, as a last resort backup in case everything else fails.

Still, those filenames were bugging me. I’d really like to store pictures and videos named by the time they were taken, for easier discoverability. Something like 20210826_120023.jpg.

I knew I just needed to use the EXIF tags in those files and some other tool to rename the file accordingly but what better motivation than this project to try out the new scripting features in Elixir 1.12?

So here is what I did.

First, I made sure the Elixir runtime was around. I have to run the script with the elixir command so it must be of course available on my path, a check running elixir -v shows that I am using Elixir 1.12, that’s good to go.

Then I created a single file with the .exs extension: at the beginning of that file I listed the script dependencies, in my case only Timex. I could have used all packages on Hex.

  {:timex, "~> 3.0"}

The first time I run the script Timex have been downloaded, compiled and cached for sequent executions.

I decided to use exiftool to read the EXIF tags of my collection as it supports out of the box many different file formats. It’s an external command line utility that I triggered through the System module. That worked well, but there was still a problem to solve: not all file types have one single EXIF tag with the date stored in it.

For that reason I decided to fallback on different EXIF tags, based on the following order: first, DateTimeOriginal is used if present, when missing I take the CreationDate tag instead, and when also that one is missing I use the ModifyDate tag.

def select_date_field(%{
      creation_date: creation_date,
      date_time_original: date_time_original,
      modify_date: modify_date,
      filename: filename
    }) do
  cond do
    Timex.is_valid?(date_time_original) ->
      %{filename: filename, date: date_time_original}

    Timex.is_valid?(creation_date) ->
      %{filename: filename, date: creation_date}

    Timex.is_valid?(modify_date) ->
      %{filename: filename, date: modify_date}

I have not researched if this is the same logic Apple Photos uses to show the image date, but it works pretty well for all the files I have in my collection.

You can find the complete script into this repository, use at your own risk (backup your collection before running it).

It takes a few minutes to rename a 3000 files collection, where most of the time is spent by exiftool to read the EXIF tags. Not so fast but it’s good enough for me.

I didn’t mean to create the fastest script or the most portable one, but spending a few hours writing a script to solve this problem was indeed a lot of fun!