MediaRescue - one-command triage for a photo library
====================================================
MediaRescue answers one question, quickly and trustworthily: which of the
photos in this folder are actually still good?
Point it at a folder. It FULLY DECODES every image it finds - not just the
header - and gives each file one plain verdict. Then, if you want, it moves
the bad ones aside so what is left is a library you can trust.
It is a desktop command-line tool for Windows and macOS (a Linux build is
included for servers and NAS boxes). There is no web version and no daemon.
THE SIX VERDICTS
----------------
HEALTHY The image decoded completely, all the way to the last pixel.
Reported with its real format and pixel dimensions.
TRUNCATED The file starts with a valid PNG/JPEG/GIF header, but the
data stops before the format's end-of-file marker (PNG IEND,
JPEG FF D9, GIF 0x3B). Classic half-copied or interrupted
download / card-pull damage.
CORRUPT The file is complete - its end-of-file marker is present -
but the image data inside is damaged or structurally
invalid, so it will not decode. Also covers files with no
recognisable image magic bytes at all (for example a text
file that has been given a .png name).
MISLABELLED The file decodes perfectly, but its real format (determined
from its magic bytes) does not match its extension: a PNG
named .jpg, a JPEG named .png. Harmless to your data,
annoying to software that trusts extensions.
EMPTY Zero bytes. There is no image in there at all.
UNREADABLE The file could not be read: permission denied, I/O error, or
a broken symbolic link.
Precedence is deliberate: damage outranks naming. A file that is both
misnamed AND broken is reported as broken, because that is the verdict that
decides whether you can still use it.
TRUNCATED versus CORRUPT is decided structurally, by whether the format's
end-of-file marker survived - not by pattern-matching the decoder's error
text. Go's decoders report a file that ran out of bytes with an ordinary
format error ("not enough pixel data", "short Huffman data"), which on its
own cannot be told apart from internal damage. The end-marker test is
consulted only after a decode has already failed, so a healthy file with
unusual trailing bytes can never be misjudged by it.
COMMANDS
--------
mediarescue scan
[--recursive] [--json]
Triage a folder. Prints one line per file plus a per-class summary
with counts and bytes. Healthy files show their format and
dimensions. Strictly read-only: nothing is created, modified, moved
or deleted. Without --recursive only the files directly in are
examined.
mediarescue quarantine --quarantine
[--classes truncated,corrupt,empty] [--recursive] [--apply]
Triage exactly as scan does, then MOVE the files whose verdict
is listed in --classes into , preserving each file's path
relative to .
DRY RUN BY DEFAULT. Without --apply nothing on disk is touched; you
get an accurate WOULD-MOVE preview and is not even created.
HEALTHY is not a selectable class: MediaRescue will not move a file
it has just declared healthy.
mediarescue verify [--json]
Full detail for a single file: verdict, extension claim, magic-byte
identity, decoded format and dimensions. Exits 0 when the verdict is
HEALTHY and 1 otherwise, so it can gate a shell script. Read-only.
mediarescue help (also -h, --help, and --help)
Exit codes: 0 success / healthy. 1 bad invocation, or a non-HEALTHY verdict
from verify, or a failed move. 2 an operand that cannot be used at all
(missing path, wrong kind of path, unknown flag or class).
EXAMPLES
--------
mediarescue scan "D:\Photos" --recursive
mediarescue scan ~/Pictures --recursive --json > triage.json
mediarescue quarantine ~/Pictures --quarantine ~/Pictures-BAD --recursive
mediarescue quarantine ~/Pictures --quarantine ~/Pictures-BAD --recursive --apply
mediarescue quarantine ~/Pictures --quarantine ~/Q --classes empty --apply
mediarescue verify ~/Pictures/IMG_0042.jpg
WHAT IS IMPLEMENTED
-------------------
* Full pixel decoding of every candidate file, not a header sniff. A file
with a perfect header but missing data is caught.
* The six verdicts above, one per file, with a plain-English reason.
* Real format identification from magic bytes, independent of the file
name, which is what makes MISLABELLED detection possible in both
directions (PNG named .jpg and JPEG named .png).
* Correct pixel dimensions and format for every healthy file.
* Per-class summary with file counts and total bytes.
* Optional recursion into subdirectories.
* Machine-readable --json output for scan, quarantine and verify.
* Quarantine that MOVES files into a mirror of your folder structure,
dry-run by default, requiring --apply to act.
* Selectable classes to quarantine, so you can move only the zero-byte
files and leave everything else alone.
* Refuses to overwrite an existing file in the quarantine directory: such
a file is reported FAILED and left untouched in the library.
* Skips the quarantine directory itself if it happens to sit inside the
library being scanned.
* Cross-device safe moves: rename where possible, otherwise a verified
copy followed by removal of the source.
* No configuration file, no network access, no telemetry, no background
service.
WHAT IS NOT IMPLEMENTED
-----------------------
* IT DOES NOT REPAIR DAMAGED FILES. Not one byte. MediaRescue tells you
the truth about a file and moves it aside; fixing it is PhotoRevive's
job, and PhotoRevive is the tool to run on your quarantine folder.
* IT DOES NOT RECOVER DELETED FILES. Undeleting requires raw block-device
access and filesystem-internal carving, which is a fundamentally
different (and far more dangerous) kind of program. MediaRescue only
ever looks at files that already exist in a folder you name.
* IT NEVER DELETES ANYTHING. There is no delete command and no delete
flag. Damaged files are moved, and only with --apply.
* ONLY THE FORMATS GO'S STANDARD LIBRARY DECODES: PNG, JPEG and GIF
(.png, .jpg, .jpeg, .gif). Files with other extensions are ignored
completely - they are neither examined nor moved.
* No RAW camera formats (CR2, NEF, ARW, DNG, ...).
* No video or audio files, and no container validation.
* No HEIC/HEIF, WebP, AVIF, TIFF or BMP.
* Animated GIFs are validated by decoding the first frame; later frames
are not individually checked.
* No EXIF parsing, no thumbnail extraction, no perceptual quality check.
A photo that decodes but is a grey smear is HEALTHY here: MediaRescue
judges file integrity, not photographic content.
* No duplicate detection, no de-duplication, no renaming, no organising.
* No undo command. The quarantine directory mirrors your original folder
structure, so putting files back is an ordinary file-manager move.
* No GUI. This is the command-line tool.
ROADMAP
-------
* RAW camera formats (CR2, NEF, ARW, DNG) with per-vendor structural
validation, so a triage covers a photographer's real working library
and not just the exported JPEGs.
* Video container validation: MP4/MOV atom-tree and MKV element walks to
flag interrupted recordings, which are the single most common thing
people hand a rescue tool after a card failure.
* Recovery from unmounted media: reading a card or disk image directly
rather than a mounted folder, so a drive the operating system refuses
to mount can still be triaged.
BUILDING
--------
Go 1.24 or newer, standard library only, no third-party dependencies:
go build -o mediarescue .
Offline builds work because there is nothing to fetch:
GOPROXY=off go build -o mediarescue .
Prebuilt binaries are in dist/:
mediarescue-windows-amd64.exe Windows 10/11, 64-bit
mediarescue-darwin-arm64 macOS, Apple Silicon
mediarescue-darwin-amd64 macOS, Intel
mediarescue-linux-amd64 Linux, 64-bit
PART OF THE TECHLOSOFT MEDIA RESCUE LAB
---------------------------------------
MediaRescue is the triage step: it decides what is broken.
PhotoRevive is the repair step: it tries to fix what MediaRescue flagged.
The intended workflow is to scan, quarantine, then point PhotoRevive at the
quarantine folder - keeping the "is it broken?" decision completely
separate from the "can it be fixed?" attempt.