Created
November 28, 2021 14:24
-
-
Save tomaspavlic/ca3a91f3c7d73c062196f842a091338d to your computer and use it in GitHub Desktop.
Sort photos by dates using EXIF metadata
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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