🖥️
← Back to KLO Software

SnapDragger

Tidy up a folder of photographs and video clips from the command line. SnapDragger reads the date each picture was actually taken from inside the file, renames everything to sortable timestamped names, files them into year and month folders, and builds an HTML contact sheet of the whole library. Nothing is renamed unless you ask for it, every run can be previewed first, and every run can be undone.

Windows Command Line JPEG · HEIC · AVIF · PNG MOV · MP4 · WMV · AVI SnapDragger running --list over a folder of 490 JPEGs, showing each iOS file name, the timestamped name it would be given, its size, and the Exif date and camera it was read from

--list over a folder of 490 photographs. Each line shows the file as it is now, the name SnapDragger would give it, its size, and — in green — the date it found inside the file and the camera that took it. Nothing has been changed.


Overview

SnapDragger turns a folder full of photo files with names like DSC00123.JPG or IMG_4471.HEIC into something you can actually find things in. It reads the moment each photograph was taken from the file itself rather than trusting the timestamp your computer shows, renames each one to a name that sorts into chronological order, files it under the year and month it belongs to, and optionally writes an HTML page of thumbnails for the whole collection.

It is a command line tool. There is no window — you point it at a folder and it tells you, line by line, what it found and what it did with it. You can also do a safety dry run where SnapDragger shows you exactly what it will do before anything is changed.

The most up to date version of this information will always be the online version.


Where the Date Comes From

A photograph taken by a modern camera or phone “knows” when it was taken. The camera saves that information inside the image file in a “packet” that SnapDragger examines:

JPEG — Exif DateTimeOriginal, and the real pixel size from the encoded frame rather than whatever a copied Exif block claims
HEIC and AVIF — the Exif block, reached through the ISO base media boxes
PNG — no capture date exists in the format, so the file time is used
MOV, MP4, M4V — Apple's creation date where the camera wrote one, otherwise the QuickTime movie header
WMV and AVI — the container's own creation date, where it has one

Where a file carries no date of its own, its filesystem timestamp is used instead, and the summary line says which, so you always know what you are looking at.

Video, and the timezone problem

Video is a little trickier. A movie header often records UTC and says nothing about where the camera was, so a clip filmed on holiday comes out hours wrong if it is simply converted to the timezone the computer is sitting in right now. SnapDragger works the offset out per recording: from a nearby clip that carries a suitable tag, which states the offset outright, or, failing that, from the photographs taken around the same time. A folder covering a trip abroad and the weeks either side of it comes out right throughout.

Offsets are rounded to whole hours, so a recording made somewhere on a half-hour offset — India or Nepal, for instance — comes out half an hour out. Rounding finely enough to reach those would fold the minutes between a photograph and a clip into the answer instead, which is wrong far more often.

Renaming and Filing

File names are handled as UTF-8 throughout, so a picture called Poreč.jpg or München.heic is read, renamed and linked under its own name rather than being mangled or skipped. Anything that looks like a picture and still cannot be read is named in the output, so nothing goes missing quietly.

Whatever you choose, the summary line, the HTML links and the place the file actually ends up all agree:

--folder=Albums        Albums\name.jpg --folder=auto          2024\01\name.jpg --folder=Albums/auto   Albums\2024\01\name.jpg
SnapDragger renaming twelve files with an IMG prefix and filing them into PhotoAlbum/2016/12 and PhotoAlbum/2012/02, finishing with the command needed to undo the run

A real run: --prefix=IMG --folder=auto --url-prefix=PhotoAlbum. Twelve files taken in December 2016 and February 2012 are renamed and filed into PhotoAlbum\2016\12 and PhotoAlbum\2012\02 according to the date inside each one — note the .MOV clip, whose date came from its QuickTime tags. The last line is the command that would put it all back again.


The Contact Sheet

SnapDragger optionally writes an HTML page with a table for each month, in date order, each cell holding a thumbnail that links to the picture itself.

A generated SnapDragger contact sheet: a February 2012 table of two photographs above a December 2016 table of ten, each thumbnail captioned with its date and linking to the picture or clip

The page produced by the run above. Each month gets its own table, oldest first, and every cell is captioned with the moment the picture was taken. A clip is labelled Play Video rather than View Photo, and its thumbnail is a frame taken from a few seconds in. (Faces blurred here for the website.)

The page is rebuilt from what is on disk rather than from whatever this particular run happened to be given, so it always describes the whole library. Add more photographs later, run it again, and only the months you actually added to are rebuilt — every other month is carried through exactly as it was. Thumbnails are made for anything on the page that lacks one, so an older file never shows a broken picture.

Thumbnails load as you scroll to them, so a month of several hundred pictures opens at once rather than waiting on every image at the top. A page is only a hundred kilobytes of HTML; it was the hundreds of separate image reads that made a big one slow, especially from a synced folder where some may not be on the disk yet. Each cell already declares the size it will be, so nothing jumps about as they arrive.

Thumbnails all share a height so a row of them lines up, and the width follows each picture's own shape, so a portrait photograph is never squashed into a landscape box. The Exif orientation is honoured, which matters more than you might think — how often have you seen people's photos stored sideways and had to awkwardly tilt your head to view them?

Making thumbnails needs ImageMagick, and clips additionally need ffmpeg. Neither is included. SnapDragger looks for them on your PATH, uses them if they are there, and quietly does without where they are not. For pictures to get their thumbnails, just having ImageMagick installed is enough; if you want that for video too, then install ffmpeg. Both are free — see Download and Install.

The page stands on its own

Everything the contact sheet needs is written into it — the stylesheet and all — so it can be mailed, copied onto a memory stick or opened years later without hunting for anything alongside it. There is a button in the top right corner that cycles follow the machine → light → dark, and it remembers which you chose, so a page kept beside these ones agrees with them about the theme.

A page whose header you wrote yourself keeps it. SnapDragger only supplies one where there was nothing above the first table, so a contact sheet you have dropped your own heading or styling onto is left as you made it.

What each table heading says

A heading carries the name, then how many pictures are under it and when they were taken:

Scans 47 pictures · Mar 1998 - Jun 2001

so a table can be judged before it is scrolled through, and two compared without counting. The range is said at the coarsest granularity that still tells you something — 12 Jan 2016 for a single day, 3-27 Jan 2016 across one month, Mar-Jun 1998 across one year, and Mar 1998 - Jun 2001 for anything wider.

It is left off where it would only repeat the heading: a table called September 2013 holding nothing but September 2013 gains nothing from a date after it, so it shows just the count. A folder whose name says nothing about when — Scans, Odds and Ends — always keeps its range, which is the case the range is really there for.

Finding your way round a long page

A page holding more than one table gets a strip of links across the top, one per table, each with its count — September 2013 9 · October 2013 41 · December 2016 23. A page with a single table gets no strip rather than a strip with one entry in it.

The strip is built by the page itself when it opens rather than written out by the program, and the reason is the way pages are rebuilt: a month at a time, with every untouched month copied through byte for byte, so nothing in SnapDragger ever holds the whole list of tables at once. By the time they are all in the one document, the browser does.

Looking through the pictures

Clicking a thumbnail shows the picture over the contact sheet rather than opening a tab. An arrow at each edge moves to the previous or next item in the same table, and disappears when there is nothing that way. The left and right keys do the same, Escape closes, and so does clicking the background. Underneath sits the caption, the file name, and how far through you are.

The point is not having to switch tabs to see the next photograph, so nothing about it opens a tab if it can avoid it. Nothing extra is written into the page to make it work either: every cell already holds the full-size path and its caption, and the cells are in a table, so “the next one” is simply the next cell along. The links keep their target="_blank", so with JavaScript turned off the page behaves exactly as it did before there was a viewer at all.

There is a slideshow too, from the button beside the close or from S or the space bar. Four seconds a picture; a movie is left to play out and moves on when it ends, rather than being cut off after the same few seconds a photograph gets. It steps the same way the arrows do, so it skips whatever they skip, and it stops at the end of the table rather than sitting on the last picture with a timer still going round.

The viewer open over a contact sheet, showing a desert photograph of Joshua trees below a ridge of boulders. An arrow sits at each edge, the play and close buttons are in the top right corner, and the line underneath reads 10-Apr-2013 14:35:28, WP_20130410_143528.jpg (51 of 145)

The viewer, with the contact sheet still behind it. The arrows move along the table and the ▶ starts the slideshow; underneath sits the caption, the file name, and how far through the table you are — 51 of 145.

Formats a browser cannot show

Other than Safari, most browsers cannot display HEIC, although the thumbnail works because SnapDragger, via ImageMagick, generates a JPEG for it. WMV, ASF and AVI no browser will play, but MP4, MOV and MPG generally work. You can, of course, convert your photos to JPEG and save the originals — see the HEIC tip below for details.

Rather than carry a list of what browsers could manage on the day the page was written, the viewer tries the file and believes the answer. If it will not decode, the viewer gets out of the way and the file opens in a tab exactly as it used to — and that format is not attempted again while the page is open, so only the first click on a given format costs anything.

This is deliberate, and it is about how long these pages last. If a browser gains HEIC one day, a page written today will start showing HEIC — nothing rebuilt, no new version of SnapDragger, and no downloads needed. A page is self-contained, needing no fonts, no scripts, no images from the internet.

Building a page and nothing else

--scanfolders builds the page from what is on disk and does nothing else: nothing is renamed and nothing is moved. Useful for indexing a library that is already tidy, or for building a fresh page after moving things about by hand. What goes in which table is up to you:

The folder form looks two folders deep, counting only folders that actually hold pictures: one holding nothing but other folders describes nothing, so it is walked through for free. That matters because --folder=auto given a base folder builds Albums\YYYY\MM — the pictures sit three folders down with the first two empty, and they are still found from the top. Folders named thumbnails and their contents are ignored other than for thumbnail image creation and display, so you can stash files you do not want to display in those folders.

A page for each folder

One page for a whole library gets slow to open long before it gets hard to read — a few thousand cells is a few thousand thumbnails, and loading them as you scroll defers the reading but not the parsing. Put a * in the name and you get a page per subfolder instead, the star standing for the folder's own name:

snapdragger --scanfolders --html=*.html
2012 Munich.html 2013 Paris.html Odds and Ends.html index.html

Each page holds every table belonging to that subfolder, however many levels down they were found, so 2016\01 and 2016\02 stay together on 2016.html rather than being scattered. Pictures sitting directly in the folder you scanned belong to no subfolder, so their page is named for that folder.

Any folder given in the pattern is kept, so --html=pages\*.htm writes the whole set into pages. The extension is kept too, which is what decides the index's name: *.html gives index.html and *.htm gives index.htm, since one set of pages with two spellings in it would only confuse. A star is the sentinel precisely because Windows will not allow one in a file name, so no real name can be mistaken for a pattern.

The way in is written last, once every page is known: one cell per page, showing that folder's first picture as its thumbnail, its name, and how many pictures it holds. The index carries the theme switch but not the picture viewer — the links here go to pages rather than to photographs, and a viewer would try to display one as though it were a picture. A folder actually called index would have taken that name already, and rather than write over a real page the run keeps it and says so.

Splitting is something only the folder grouping can do: a table per month comes from the dates rather than from the folders, so there is nothing to name a page after. Asking for both is refused rather than quietly given one page.

A generated contact sheet in the dark theme. A strip of links across the top reads 2017_07_Maui 306 and Snorkelling Maui - 2017-07-27 266; the table below is headed 2017_07_Maui, 306 pictures, Jul-Dec 2017, over rows of thumbnails each captioned View Photo and the date and time it was taken

A generated page, in the dark theme. The strip along the top links to each table with its count; the heading gives the folder's name, how many pictures are under it and when they were taken — 306 pictures · Jul-Dec 2017.


Naming a Folder

A folder name and a file spec look exactly alike on a command line, and only the directory part of a file spec — everything up to the last backslash — says which folder to work in. So if a bare folder name really is a folder then everything in it is what to work on, and the run clearly says so:

snapdragger --scanfolders ".\Holiday Pictures" --html=Holidays.htm Looking in folder: C:\Users\...\Pictures\Holiday Pictures Read as: .\Holiday Pictures is a folder, so everything in it: .\Holiday Pictures\*.*

A wildcard can never name a folder — no file system here allows * or ? in a name — so every file spec that already worked is untouched. The folder may also be given on the option itself, as --scanfolders="Holiday Pictures"; the words folders and dates keep their meaning there, so a folder named one of those two has to be given as the file spec instead.

A quotation mark in an argument

A quoted path ending in a backslash escapes its own closing quote. The quoted string then never closes and runs on into whatever followed it, so this:

snapdragger --scanfolders ".\Holiday Pictures\" --html=Holidays.htm

arrives as one argument reading Holiday Pictures" --html=, with the option that was swallowed simply gone. This is the Windows argument parser rather than SnapDragger or the command prompt, and it catches every program the same way — which is why the failure that follows never seems to have anything to do with the cause.

By the time a program sees its arguments the backslash has gone and a literal quotation mark is left in its place. That is the only trace there is, so that is what SnapDragger looks for; and since Windows permits no quotation mark in a file name, finding one is never a false alarm:

Check the quoting: .\Holiday Pictures" --html=Holidays.htm a " cannot be in a Windows file name, so this is one argument that was meant to be two or more. A quoted path ending in \ escapes its own closing quote, so "folder\" runs on and swallows what follows it. Write "folder\*.*" instead, or double the backslash.

It is a warning rather than a refusal: by that point the arguments have been merged and nothing can un-merge them, so the run carries on and fails on its own terms.


Tip: A Folder Full of HEIC Pictures

If you want to convert a folder of .heic images to .jpg so you can view them all in your browser, and have SnapDragger build the pages for them, this procedure works well. Starting with your command window in the folder holding the .heic files, convert them with ImageMagick and then move the originals into the thumbnails folder. That way you still have them, but they are not included in the SnapDragger tables:

mkdir thumbnails & magick mogrify -format jpg -auto-orient -quality 92 *.heic & for %f in (*.heic) do @if exist "%~nf.jpg" move "%f" thumbnails\

It works because SnapDragger never reads a thumbnails folder — it is where the generated thumbnails live, and reading it would list every picture twice. So anything put in there is invisible to the contact sheet while staying exactly where you can find it. The -auto-orient turns each picture the way its Exif says it should be shown, and the if exist moves an original only once its JPEG has actually appeared, so a conversion that fails leaves that file where it is.

One thing to watch if you are typing this into a .cmd file rather than at the prompt: the loop variable has to be doubled there, so %f becomes %%f and %~nf becomes %%~nf. Left single it fails with was unexpected at this time.

HEIC support in ImageMagick comes from the libheif delegate, which the standard Windows installer includes. If nothing converts, check with:

magick -list format | findstr HEIC

A line reading HEIC HEIC r-- is all this needs. The r is for reading, which is the direction that matters — the JPEGs come out of the JPEG encoder. A build that cannot write HEIC, which is the common case, still converts out of it perfectly well.

Lastly, a folder called thumbnails looks disposable: it is exactly the sort of thing that gets deleted to reclaim space, so keep that in mind if the originals matter to you.


Nothing Happens That You Did Not Ask For

A SnapDragger dry run showing the name each of twelve files would be given, and a closing line reading DRY RUN - nothing was renamed, moved or created

A --dry-run: every name each file would be given is shown in green, and the run finishes by saying plainly that nothing was renamed, moved or created. Run it again without --dry-run to do it for real.


Undoing a run

If a run has done something you did not want, do not worry; it can be undone. Every run that moves or renames anything writes a plain-text log beside the files and finishes by telling you how to use it:

To undo this run: snapdragger --revert snapdragger_log_20260728_133709.txt

Undoing puts every file back under its old name, removes the thumbnails that run created, and takes away any folder it emptied. A file already sitting where one belongs is left alone rather than written over, and reported. If you use --dry-run first, that will show everything that would be done but without changing anything, so that is a safety mechanism too.

Every effort is made to ensure that your photos are safe at every step, but of course you should make a complete backup of your pictures before using any tool on them, SnapDragger included.

Looking Inside a File

--info shows the information a file carries within itself, and changes nothing. The camera and lens, every date it carries, the settings it was taken with, and — where the camera recorded one — the GPS position:

=== WP_20130922_002.jpg ===
        File size : 2.587 MB (2713060 bytes)
      Camera make : Nokia
     Camera model : Lumia 920
             Lens : 
         Software : Windows Phone
            Taken : 2013:09:22 08:23:10
      Orientation : 1 (upright)
       Resolution : 2000 x 3552 pixels
        ISO speed : 100
    Exposure time : 1/289 second
         Aperture : f/2.0
     Focal length : 
            Flash : did not fire
    Metering mode : 
 Exposure program : 
      Description : 
          Comment : 
     GPS latitude : N  49d 27m 36.284s
    GPS longitude : E  11d  5m 13.091s
     GPS position : 49.460079, 11.086970
     GPS altitude : 0.0 m above sea level
                    Main block
     X resolution : 72
     Y resolution : 72
  Resolution unit : 2
YCbCr positioning : 1
       Tag 0xEA1C : 2060 bytes
                    Exif block
     Exif version : 4 bytes
   Components cfg : 4 bytes
    Exposure bias : 0/6 (0)
       Maker note : 5231 bytes
 Flashpix version : 4 bytes
     Colour space : 1
 Interoperability : 9805
       Tag 0xEA1C : 2060 bytes
                    GPS block
      GPS version : 4 bytes
 GPS measure mode : 3
     GPS accuracy : 10000/130000 (0.0769231)
       Tag 0xEA1C : 1888 bytes
                    Thumb block
      Compression : 6
     X resolution : 72
     Y resolution : 72
  Resolution unit : 2
 Thumbnail offset : 12008
 Thumbnail length : 12708

One photograph, taken in Nuremberg on a Nokia Lumia 920. This is a real run captured to a file and turned into HTML; only the run's opening banner and closing summary are trimmed, since the captures above already show those.

The labels are right aligned so the colons line up, and the file name is in a colour nothing else uses, so a run over a folder breaks cleanly into one block per picture.

The first block is always the same shape, blank where the file said nothing, so two files can be read down side by side and a missing camera is as plain as a present one. Every date the file carries is listed under the name of where it came from rather than only the one a new name would be built from — a file disagreeing with itself is the usual explanation for a name that looks wrong.

The position block appears only where there is one. It is given both as degrees, minutes and seconds to read, and as a signed decimal pair to paste into a map. Exif stores the hemisphere as a letter with the number always positive, so the sign is worked out here — otherwise everywhere south and west lands in the wrong quarter of the world.

Then whatever else the file turned out to carry, grouped by the block it came from: the main image, the Exif block, the GPS block, and the thumbnail's own. A tag with a name gets it; one without is shown by its number, as Tag 0xEA1C : 2060 bytes, because a file holding something the program does not recognise is worth knowing about — and hiding it is the one thing an option called --info must not do. Around a hundred tags are named.

A tag being of type “ASCII” is not a promise that it holds text. Some phones write several hundred bytes of private binary into the description and comment fields, so anything whose content is not readable text is reported by its size instead — 956 bytes, not text — rather than being printed at your terminal. Accented text is comfortably inside the test: Nürnberg is two bytes in ten outside ASCII, where those private blocks are three fifths.

Colour, and Keeping It

The output is colour-coded: the file as it is now, the name it is being given, where the date came from, and anything that needs your attention. On Windows that colour is a property of the console rather than part of what the program writes, so redirecting a run to a file has always produced clean plain text — correct, but no use to anything that wanted the colours.

--color=always writes the colour into the output instead, as ANSI escape sequences, wherever that output is going. A captured run keeps its colours, which is what these are — real runs, captured to a file and turned into HTML, not screenshots:

--list over a folder: fifteen files found, twelve of them pictures, nothing changed. Each line gives the file as it is now, the name it would be given, its size, and where the date came from.

SnapDragger v3.0.4
Command line arguments: --color=always --list
     Looking in folder: C:\LocalOnly\Snappy
20161201_154932020_iOS.jpgWP_20161201_074932.jpg      2.319 MB (EXIF: 2016-12-01 07:49:32, Apple iPhone 6s)
20161201_185438509_iOS.jpgWP_20161201_105438.jpg      2.302 MB (EXIF: 2016-12-01 10:54:38, Apple iPhone 6s)
20161201_190227427_iOS.jpgWP_20161201_110227.jpg      2.678 MB (EXIF: 2016-12-01 11:02:27, Apple iPhone 6s)
20161202_031930377_iOS.jpgWP_20161201_191930.jpg      1.139 MB (EXIF: 2016-12-01 19:19:30, Apple iPhone 6s)
20161202_032005579_iOS.jpgWP_20161201_192005.jpg      1.098 MB (EXIF: 2016-12-01 19:20:05, Apple iPhone 6s)
20161202_032159000_iOS.MOVWP_20161201_192159.MOV      110.3 MB (QT: 2016-12-01 19:21:59; QuickTime mvhd 2016-12-01 19:22:00)
20161202_032345127_iOS.jpgWP_20161201_192345.jpg      973.2 KB (EXIF: 2016-12-01 19:23:45, Apple iPhone 6s)
20161202_032427273_iOS.jpgWP_20161201_192427.jpg      955.5 KB (EXIF: 2016-12-01 19:24:27, Apple iPhone 6s)
20161202_032433941_iOS.jpgWP_20161201_192433.jpg      1.274 MB (EXIF: 2016-12-01 19:24:33, Apple iPhone 6s)
20161202_032452242_iOS.jpgWP_20161201_192452.jpg      987.4 KB (EXIF: 2016-12-01 19:24:52, Apple iPhone 6s)
                IMG001.jpgWP_20120221_113248.jpg      537.2 KB (EXIF: 2012-02-21 11:32:48)
                IMG002.jpgWP_20120222_185418.jpg      948.5 KB (EXIF: 2012-02-22 18:54:18)
Given file spec (*.*) matched 15 files
Number of files actually processed was 12
Number of files renamed was 0
Number of files skipped was 3
No HTML page created - use --html to ask for one

Renaming and filing for real with --folder=auto --url-prefix=Albums: twelve files sorted into Albums\2016\12 and Albums\2012\02 by the date inside each one. The last line is the command that would put them all back.

SnapDragger v3.0.4
Command line arguments: --color=always --folder=auto --url-prefix=Albums
     Looking in folder: C:\LocalOnly\Snappy
20161201_154932020_iOS.jpgAlbums/2016/12/WP_20161201_074932.jpg      2.319 MB (EXIF: 2016-12-01 07:49:32, Apple iPhone 6s)
20161201_185438509_iOS.jpgAlbums/2016/12/WP_20161201_105438.jpg      2.302 MB (EXIF: 2016-12-01 10:54:38, Apple iPhone 6s)
20161201_190227427_iOS.jpgAlbums/2016/12/WP_20161201_110227.jpg      2.678 MB (EXIF: 2016-12-01 11:02:27, Apple iPhone 6s)
20161202_031930377_iOS.jpgAlbums/2016/12/WP_20161201_191930.jpg      1.139 MB (EXIF: 2016-12-01 19:19:30, Apple iPhone 6s)
20161202_032005579_iOS.jpgAlbums/2016/12/WP_20161201_192005.jpg      1.098 MB (EXIF: 2016-12-01 19:20:05, Apple iPhone 6s)
20161202_032159000_iOS.MOVAlbums/2016/12/WP_20161201_192159.MOV      110.3 MB (QT: 2016-12-01 19:21:59; QuickTime mvhd 2016-12-01 19:22:00)
20161202_032345127_iOS.jpgAlbums/2016/12/WP_20161201_192345.jpg      973.2 KB (EXIF: 2016-12-01 19:23:45, Apple iPhone 6s)
20161202_032427273_iOS.jpgAlbums/2016/12/WP_20161201_192427.jpg      955.5 KB (EXIF: 2016-12-01 19:24:27, Apple iPhone 6s)
20161202_032433941_iOS.jpgAlbums/2016/12/WP_20161201_192433.jpg      1.274 MB (EXIF: 2016-12-01 19:24:33, Apple iPhone 6s)
20161202_032452242_iOS.jpgAlbums/2016/12/WP_20161201_192452.jpg      987.4 KB (EXIF: 2016-12-01 19:24:52, Apple iPhone 6s)
                IMG001.jpgAlbums/2012/02/WP_20120221_113248.jpg      537.2 KB (EXIF: 2012-02-21 11:32:48)
                IMG002.jpgAlbums/2012/02/WP_20120222_185418.jpg      948.5 KB (EXIF: 2012-02-22 18:54:18)
Given file spec (*.*) matched 17 files
Number of files actually processed was 12
Number of files renamed was 12
Number of files skipped was 5
Moved 12 renamed files to subfolders with date-format layout, YYYY/MM/
No HTML page created - use --html to ask for one
To undo this run: snapdragger --revert snapdragger_log_20260730_163934.txt

A --dry-run with --dedupe and a contact sheet: the duplicate search, every name that would be given and every folder that would be made — without a single file being touched.

SnapDragger v3.0.4
Command line arguments: --color=always --html=index.htm "--url-prefix=Family Albums" --folder=auto --dedupe --dry-run
     Looking in folder: C:\LocalOnly\Snappy

Looking for identical files among the 13 found...
No duplicates were deleted.

20161201_154932020_iOS.jpgFamily Albums/2016/12/WP_20161201_074932.jpg      2.319 MB (EXIF: 2016-12-01 07:49:32, Apple iPhone 6s)
20161201_185438509_iOS.jpgFamily Albums/2016/12/WP_20161201_105438.jpg      2.302 MB (EXIF: 2016-12-01 10:54:38, Apple iPhone 6s)
20161201_190227427_iOS.jpgFamily Albums/2016/12/WP_20161201_110227.jpg      2.678 MB (EXIF: 2016-12-01 11:02:27, Apple iPhone 6s)
20161202_031930377_iOS.jpgFamily Albums/2016/12/WP_20161201_191930.jpg      1.139 MB (EXIF: 2016-12-01 19:19:30, Apple iPhone 6s)
20161202_032005579_iOS.jpgFamily Albums/2016/12/WP_20161201_192005.jpg      1.098 MB (EXIF: 2016-12-01 19:20:05, Apple iPhone 6s)
20161202_032159000_iOS.MOVFamily Albums/2016/12/WP_20161201_192159.MOV      110.3 MB (QT: 2016-12-01 19:21:59; QuickTime mvhd 2016-12-01 19:22:00)
20161202_032345127_iOS.jpgFamily Albums/2016/12/WP_20161201_192345.jpg      973.2 KB (EXIF: 2016-12-01 19:23:45, Apple iPhone 6s)
20161202_032427273_iOS.jpgFamily Albums/2016/12/WP_20161201_192427.jpg      955.5 KB (EXIF: 2016-12-01 19:24:27, Apple iPhone 6s)
20161202_032433941_iOS.jpgFamily Albums/2016/12/WP_20161201_192433.jpg      1.274 MB (EXIF: 2016-12-01 19:24:33, Apple iPhone 6s)
20161202_032452242_iOS.jpgFamily Albums/2016/12/WP_20161201_192452.jpg      987.4 KB (EXIF: 2016-12-01 19:24:52, Apple iPhone 6s)
                IMG001.jpgFamily Albums/2012/02/WP_20120221_113248.jpg      537.2 KB (EXIF: 2012-02-21 11:32:48)
                IMG002.jpgFamily Albums/2012/02/WP_20120222_185418.jpg      948.5 KB (EXIF: 2012-02-22 18:54:18)
Given file spec (*.*) matched 13 files
Number of files actually processed was 12
Number of files that would be renamed was 12
Number of files skipped was 1
Would move 12 renamed files to subfolders with date-format layout, YYYY/MM/
HTML table would be written to index.htm
*** DRY RUN - nothing was renamed, moved or created. Run again without --dry-run to do it for real. ***

Being escape sequences rather than a picture, a capture like that is still text: searchable, selectable, and sharp at any size. It also means a run stays coloured through a pipe, into a pager, or in a build log.

Only the colour is affected. The running Found: and Processing: counts are drawn on a console alone, because they are redrawn in place and the backspaces that do it would be litter in a captured file.

--color=never, or the NO_COLOR environment variable, turns colour off everywhere. The default, --color=auto, is what has always happened: colour on a console, plain text into a file.


Command Line Reference

Every option has a spelled-out name; six of them also keep a single letter. Run snapdragger --help for the full built-in help, which is where this summary comes from.

snapdragger [--list] [--info] [--timesource=source] [--prefix[=text]] [--suffix=text] [--folder=name] [--dedupe] [--dry-run] [--first=n] [--small[=pixels]] [--html=file] [--url-prefix=text] [--thumbnails=when] [--label=date|name] [--scanfolders[=how]] [--revert=log] [--color=when] [--help] [wildcard_in]

Choosing files and reporting

Where the date comes from

Windows can rewrite a creation time when files are renamed — a filesystem feature called tunnelling — so created may not survive a second run. auto falls back to modified.

Renaming and filing

The HTML contact sheet

Colour

Putting a run back

Coloured output can be turned off by setting the NO_COLOR environment variable, or with --color=never, and kept when the output is redirected with --color=always.


Examples

Look at what is in a folder, changing nothing:

snapdragger --list *.jpg

Rename by the date inside each file, with an IMG prefix, and file everything into the YYYY\MM tree:

snapdragger --prefix=IMG --folder=auto *.jp*

See exactly what that would do, without touching anything:

snapdragger --prefix=IMG --folder=auto --dry-run *.jp*

Try it on just the first ten files before committing to the lot:

snapdragger --prefix=IMG --first=10 *.jp*

Rename a folder holding both originals and reduced copies, keeping the two apart rather than letting them collide:

snapdragger -r --small *.jpg

Start the name with the year rather than a prefix, and keep the _iOS that the phone put on the end:

snapdragger --prefix= --suffix=_iOS *.MOV

Only files that carry their own date, skipping the rest:

snapdragger -t=embedded --list *.*

Build a contact sheet for a library that is already tidy, changing nothing else:

snapdragger --html index.html --scanfolders

The same, but with a table per month worked out from the dates rather than from the folders:

snapdragger --html index.html --scanfolders=dates

The same again, but giving each subfolder its own page rather than one enormous one, with an index.html to open first:

snapdragger --scanfolders --html=*.html

Scan one folder rather than the current one, and open the page when it is built:

snapdragger --scanfolders "Holiday Pictures" --html=holiday.htm --open

Capture a run to a file and keep the colours in it:

snapdragger --color=always --list *.jpg > run.txt 2>&1

Features

The Real Capture Date

Read from inside JPEG, HEIC, AVIF, MOV, MP4, M4V, WMV and AVI files, not from the timestamp Windows shows.

Sortable Names

Renames to <prefix>_YYYYMMDD_HHMMSS, so a folder listing is a timeline.

Year and Month Folders

Files everything into a YYYY\MM tree, optionally underneath a folder of your own choosing.

Monthly Contact Sheet

An HTML page with one table per month, in date order, each thumbnail linking to the picture itself.

Incremental Rebuild

Add more photographs later and only the affected months are rebuilt; the rest of the page is untouched.

Orientation Honoured

Pictures taken sideways are shown the right way up, and each thumbnail is sized to its own picture's shape.

Holiday Timezones

Works out a video's offset from UTC per recording, so a trip abroad no longer comes out hours wrong.

Preview Anything

--dry-run shows exactly what would happen, clash suffixes and all, without touching a thing.

Undo a Run

Every run that moves anything writes a log, and --revert puts every file back where it came from.

Find Duplicates

Compares files in full, not by checksum, and offers to send the copies to the Recycle Bin.

Read-Only Mode

--list reports on a folder and changes nothing — and says so if an option would.

Readable Output

Colour-coded lines telling you where each date came from and what took the picture, in lined-up columns.

Look Inside a File

--info shows every date, setting and tag a photograph carries, and where it was taken.

Colour You Can Keep

--color=always puts the colour into the output itself, so a run captured to a file stays coloured.

Grouped By Folder

--scanfolders gives every folder its own table, named for it — or a table per month, as you prefer.

A Page Per Folder

--html=*.html splits a big library into one page per subfolder, with an index showing each one's first picture.

Slideshow

Press S in the viewer and it runs itself, four seconds a picture, letting each movie play out in full.

Flick Through Them

Click a thumbnail and page along the month with the arrow keys, without ever leaving the contact sheet.

A Page That Travels

The contact sheet carries its own styling and a light and dark switch, so it works wherever you put it.

Names In Any Language

UTF-8 throughout, so accents and non-Latin scripts in file names are handled rather than mangled.


Download and Install

SnapDragger

The app can be installed from:

Requires Windows 10 version 1809 or later, or Windows 11. Both x86 and x64 are supported. Run it from Command Prompt, PowerShell or Windows Terminal.

Optional: thumbnail tools

SnapDragger does everything except making thumbnails without any help. For thumbnails it uses two free tools, neither of which is included. It finds them on your PATH, so they need to be somewhere Windows looks:

With only ImageMagick installed, pictures get their thumbnails and movies do not. With neither, no thumbnails are made at all and the run carries on regardless — the page is still written, and the links to the pictures themselves still work.

← Back to KLO Software