Skip to content

Instantly share code, notes, and snippets.

@tomaspavlic
Created November 28, 2021 14:24
Show Gist options
  • Save tomaspavlic/ca3a91f3c7d73c062196f842a091338d to your computer and use it in GitHub Desktop.
Save tomaspavlic/ca3a91f3c7d73c062196f842a091338d to your computer and use it in GitHub Desktop.
Sort photos by dates using EXIF metadata
#r "nuget: MetadataExtractor"
open System
open System.IO
open MetadataExtractor
open MetadataExtractor.Formats.Exif
module Exif =
type private Metadata = Map<string, MetadataExtractor.Directory>
let private tryGetter (success, value) =
match success with
| true -> Some value
| false -> None
let extract file : Metadata =
ImageMetadataReader.ReadMetadata(filePath = file)
|> Seq.map (fun dir -> dir.Name, dir)
|> Map.ofSeq
let date (metadata : Metadata) : DateTime option =
metadata
|> Map.tryFind "Exif SubIFD"
|> Option.bind (fun dir -> dir.TryGetDateTime(ExifDirectoryBase.TagDateTimeOriginal) |> tryGetter)
module Sorter =
type FilePath = string
type SortAction = FilePath * FilePath
let rec getFiles folder : string seq =
seq {
yield! Directory.GetFiles folder
let next =
Directory.GetDirectories folder
|> Seq.map getFiles
|> Seq.fold Seq.append Seq.empty
yield! next
}
let getPhotos folder =
getFiles folder
|> Seq.filter (fun file -> file.EndsWith "jpg")
let dateToPath (destinationRootPath: string) (filePath: string) (date: DateTime) =
let fileName = Path.GetFileName filePath
let destinationPath = Path.Combine(destinationRootPath, $"{date.Year}/{date.Month}/{date.Day}", fileName)
destinationPath
let run sourceRootFolder destinationRootFolder : seq<SortAction> =
getPhotos sourceRootFolder
|> Seq.choose (fun photo ->
Exif.extract photo
|> Exif.date
|> Option.map (fun date ->
photo, dateToPath destinationRootFolder photo date))
let move sortActions =
sortActions
|> Seq.iter (fun (source, destination) ->
printfn "Moving file '%s' to '%s'" source destination
Path.GetDirectoryName destination |> Directory.CreateDirectory |> ignore
File.Move(source, destination))
Sorter.run "/Volumes/DATA/Photos/pictures_to_be_sorted/" "/Volumes/DATA/Photos/pictures_sorted"
|> Sorter.move
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment