- PowerShell 100%
|
|
||
|---|---|---|
| .github | ||
| scripts | ||
| .gitattributes | ||
| AGENT-README.md | ||
| CODE_OF_CONDUCT.md | ||
| CONTRIBUTING.md | ||
| LICENSE | ||
| README.md | ||
| SECURITY.md | ||
| THIRD-PARTY-NOTICES.md | ||
camera-roll-tools
A growing collection of PowerShell tools for keeping a camera roll organized. Cameras and phones tend to dump every capture into a single folder; these scripts help sort that pile into a clean, date-navigable structure and (over time) handle related chores like deduplication and separating videos from stills.
Scripts
| Script | Purpose |
|---|---|
scripts/Move-PhotosByDate.ps1 |
Moves date-named files (yyyyMMdd[sep]HHmmss.*) into Year\yyyy-MM-dd subfolders. |
Additional scripts (for example duplicate detection and video sorting) may be added to scripts/ over time.
Requirements
- Windows PowerShell 5.1 or PowerShell 7+
- No third-party modules; the scripts use only built-in cmdlets and .NET types
Move-PhotosByDate
Given files whose names begin with a yyyyMMdd date followed by an HHmmss time (for example 20260711-123456.jpg), the script moves each file into a per-day subfolder nested under a per-year folder, keeping the original filename intact:
C:\CameraRoll\20260711-123456.jpg -> C:\CameraRoll\2026\2026-07-11\20260711-123456.jpg
The separator between the date and time is flexible: a dash, underscore, dot, or space, or no separator at all. All of these are recognized:
20260711-123456.jpg 20260711_123456.jpg 20260711.123456.jpg 20260711 123456.jpg 20260711123456.jpg
The date for each file is resolved in this order of precedence:
- The leading
yyyyMMdd-HHmmsstimestamp in the filename. - If the filename has no valid date and
-ExifFallbackis supplied, the embedded capture date read from the file's metadata (EXIFDateTimeOriginalfor photos, "Media created" for videos).
Only files whose extension is in the processing set are considered. The default set is a list of common image extensions; -IncludeVideo adds common video extensions, and -Extension replaces the image list.
Quick start
# Preview the moves without changing anything
.\scripts\Move-PhotosByDate.ps1 -SourceFolder 'C:\CameraRoll' -WhatIf
# Reorganize images in place (Year\yyyy-MM-dd folders created under the source folder)
.\scripts\Move-PhotosByDate.ps1 -SourceFolder 'C:\CameraRoll'
# Include videos and fall back to embedded capture dates when the filename has no date
.\scripts\Move-PhotosByDate.ps1 -SourceFolder 'C:\CameraRoll' -IncludeVideo -ExifFallback
# Sort an incoming dump into an existing camera roll, only specific extensions
.\scripts\Move-PhotosByDate.ps1 -SourceFolder 'C:\Incoming' -DestinationFolder 'C:\CameraRoll' -Extension jpg,heic,dng
Parameters
| Parameter | Required | Description |
|---|---|---|
-SourceFolder |
Yes | Folder to scan for media files. Must exist. |
-DestinationFolder |
No | Root under which the Year\yyyy-MM-dd subfolders are created. Defaults to the source folder (in-place reorganization). Created automatically if it does not exist. |
-Extension |
No | Extensions to process, with or without a leading dot. Replaces the default image list. Default: common image extensions (.jpg .jpeg .png .heic .heif .tif .tiff .gif .bmp .webp .dng .cr2 .cr3 .nef .arw .orf .rw2 .raf .sr2 .raw). |
-IncludeVideo |
No | Also process common video extensions (.mp4 .mov .m4v .avi .mkv .mpg .mpeg .wmv .3gp .3g2 .webm .mts .m2ts) in addition to the images in effect. |
-ExifFallback |
No | When a filename has no valid date, read the capture date from file metadata (EXIF DateTimeOriginal for photos, "Media created" for videos). The filename date always wins when present and valid. |
-Recurse |
No | Also process files in subdirectories of the source folder. Files already in a correctly-named Year\yyyy-MM-dd folder are left untouched. |
The script supports -WhatIf and -Confirm, so every move can be previewed before it runs.
How the EXIF/metadata fallback works
-ExifFallback reads the embedded capture date without any third-party module:
- Photos: true EXIF
DateTimeOriginal(tag0x9003) viaSystem.Drawing, falling back to the Windows Shell "Date taken" property whenSystem.Drawingis unavailable (for example under PowerShell 7). - Videos: the Windows Shell "Media created" property (videos carry no EXIF).
The Windows Shell property system is the same metadata shown on the Details tab in File Explorer, accessed through the Shell.Application COM object, so it works under both Windows PowerShell 5.1 and PowerShell 7.
Behavior and safety
- Files whose extension is not in the processing set are excluded (reported at verbose level).
- Files whose names lack a valid date and, when
-ExifFallbackis set, have no usable metadata date, are skipped and reported as warnings. - The date portion is validated as a real calendar date; impossible dates (for example
2026-13-01) are skipped. - A file is skipped if a file with the same name already exists at the target path (no overwrite).
- A file already located in its correct target folder is left untouched.
- On completion the script returns a summary object with
SourceFolder,DestinationFolder,Moved,ResolvedByExif,Skipped, andExcludedcounts.
Assumptions
- The filename date is taken from the first 8 digits of the name (
yyyyMMdd) and always takes precedence over metadata. - Metadata dates are only consulted when
-ExifFallbackis supplied and the filename has no valid date.
Getting the help
Each script ships with comment-based help:
Get-Help .\scripts\Move-PhotosByDate.ps1 -Full
License
Released under the MIT License.