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.