This tool browses every Midjourney image you ever generated — chronologically, with the prompt, parameters, job ID, and reference ("image prompt") pictures for each one — without ever unpacking the 38 GB of backup zips.
| thing | location | synced? |
|---|---|---|
| original images & videos | the backup zips in iCloud Drive / Midjourney Backup / |
yes — iCloud |
| index (prompts, dates, job IDs), thumbnails, downloaded reference images | Midjourney Backup / _viewer-data / — built from the zips, lives next to them |
yes — iCloud |
| viewer code + server | this git repo (mj-archive/, tools/mj-archive-*.mjs) |
yes — git |
| per-computer backup-folder path | mj-archive/local/config.json (created by the import panel's "change…" button) |
no — local to each computer |
The viewer never stores full-size images on disk: when you open one, the site
server (npm run dev, port 8004) streams that single file straight
out of its zip with unzip -p. The grid uses small thumbnails
from _viewer-data/thumbs/. Prompts come from metadata Midjourney
embeds inside every PNG (videos have none, so their prompts are recovered from
the filename and marked "approximate").
Midjourney Backup folder in iCloud Drive (any name ending in .zip works; don't rename old ones).
Import is incremental and safe to re-run: zips that were already indexed are
skipped, and re-running never duplicates anything. The same thing from the
terminal: npm run mj:index then npm run mj:refs.
When a generation used pictures as part of its prompt, those show under
image prompts in the lightbox. They come from short
s.mj.run links, which the import step tries to download to
_viewer-data/image_prompts/. Three outcomes:
Links hosted on cdn.midjourney.com refuse command-line downloads
(a Cloudflare bot check) but serve real browsers happily — so as the final
import step, the viewer page itself automatically downloads those
references and hands them to the server. Keep the tab open until the
import status line says the reference fetch finished. The import panel also
has a fetch them now button to run that step by itself at any
time.
Fallback (only if the automatic fetch ever stops working):
open any cdn.midjourney.com reference image in a browser tab
(click one in a lightbox; a "Just a moment" page may flash first), then paste
this into that tab's devtools console (⌥⌘J in Chrome):
fetch('http://localhost:8004/mj-archive/ref-runner.js')
.then((r) => r.text()).then(eval)
npm run dev.Midjourney Backup folder (including _viewer-data).http://localhost:8004/mj-archive/ — it should just work, because the index and thumbnails sync through iCloud; nothing needs rebuilding.mj-archive/local/config.json (the MJ_BACKUP_DIR environment variable overrides everything if set).| symptom | fix |
|---|---|
| "index not built yet" or an empty page | Make sure you opened it through the server (npm run dev → localhost:8004), not as a file. Then click import. |
| "folder not found" in the import panel | iCloud Drive path differs on this computer — click change… and select the Midjourney Backup folder. |
| images slow to open on a new computer | iCloud is still downloading the zips ("Optimize Mac Storage" keeps them in the cloud until first use). They materialize on demand. |
| a reference image says "no local copy yet" | Open the import panel and click fetch them now (or run a full import). References that stay missing are usually expired Discord attachments — gone for everyone. |
| prompt looks mangled on a video | Expected — videos carry no embedded metadata, so the prompt is reconstructed from the filename ("recovered from filename" note). |