Skip to content

Instantly share code, notes, and snippets.

@malcolmsparks
Last active June 29, 2021 19:10
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save malcolmsparks/61418b6bbcd0962536add1ccb07033b5 to your computer and use it in GitHub Desktop.
Save malcolmsparks/61418b6bbcd0962536add1ccb07033b5 to your computer and use it in GitHub Desktop.
A babashka script for sorting and de-duping a photo collection on Linux.
#_( ;; Allow this script to be executed directly
"exec" "bb" -o "--classpath" "." "$0" "$@"
)
;; Copyright © 2020, Malcolm Sparks
;; Permission is hereby granted, free of charge, to any person obtaining a copy
;; of this software and associated documentation files (the “Software”), to deal
;; in the Software without restriction, including without limitation the rights
;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
;; copies of the Software, and to permit persons to whom the Software is
;; furnished to do so, subject to the following conditions:
;; The above copyright notice and this permission notice shall be included in
;; all copies or substantial portions of the Software.
;; THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
;; SOFTWARE.
;; I have thousands of old photos, unsorted, on various computers, phones, usb
;; keys and external hard-drives. I occasionally make backups so I've got loads
;; of duplicate files too. I've bought a big hard-drive (4Tb) which is big
;; enough to them all.
;; Requirements:
;; 1. I want to sort and de-dupe my photo collection
;; 2. I want to sort them by date, grouped by year-month
;; 3. By date, I mean the date I took the picture.
;; 4. I don't want two backups of the same picture (de-dupe).
;; 5. If successive photos were captured in the same second (my Canon 6D can
;; manage that), I don't want them treated as duplicates
;; 6. I don't want to spend time to evaluate/install/learn/use any software
;; which may have different goals to mine. I want to script this!
(def destdir (io/file "/tmp/immv"))
(defn exiv2-date
"Extract the date from the photo. Available as exiv2 0.27.3-1"
[arg]
(:out (shell/sh "exiv2" "-g" "Exif.Image.DateTime" arg)))
(defn b2sum
"Compute the BLAKE2 message digest. Available in coreutils 8.21-1."
[arg]
(:out (shell/sh "b2sum" arg)))
(defn props [arg]
(let [[_ suffix] (re-matches #".*\.(\p{Alnum}+)" arg)
[_ _ _ dt tm] (str/split (exiv2-date arg) #"\s+")
[_ year month day] (re-matches #"(\d{4}):(\d{2}):(\d{2})" dt)
[_ hour min sec] (re-matches #"(\d{2}):(\d{2}):(\d{2})" tm)]
{:original arg
:target-subdir (format "%s-%s" year month)
:target-filename
(format
"%s%s%s-%s%s%s-%s.%s" ; my preferred final filename-format
year month day
hour min sec
;; First 8 chars should do, I don't want huge filenames.
(subs (b2sum arg) 0 8)
suffix)}))
;; Example: backup-photos *.JPG *.CR2
(doseq [arg *command-line-args*
:let [{:keys [original target-subdir target-filename]} (props arg)
todir (io/file destdir target-subdir)
_ (.mkdirs todir)
tofile (format "%s/%s" todir target-filename)
cmd "cp" ;; can change to mv if you like
args [cmd original tofile]]]
(if (and
(.exists (io/file tofile))
;; In case the script was cancelled during a run, make sure we don't have
;; 'half' a file.
(= (.length (io/file original))
(.length (io/file tofile))))
(println "Skipping" original "as" tofile "already exists")
(do
(println (case cmd "cp" "Copying" "mv" "Moving") original "to" tofile)
(apply shell/sh args))))
;; Local Variables:
;; mode: clojure
;; End:
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment