6.9 KiB
6.9 KiB
Video upgrade pass — plan and findings (2026-10-14)
Status: designed and partly measured, NOT built. Nothing in the archive has been changed.
Written so this can be picked up cold. Account context: the scraping account is the
fresh erg05teur (see docs/gallery-dl.md, top of "The account was suspended…") — every
request in this plan is spent on a new account, so the budget rules there apply in full.
Why this exists
The question was whether a logged-out fetch had given us lower-resolution media and whether a logged-in run could upgrade it. Measured result:
- Images: no upgrade needed. Header-scan of 38.9k archived images, plus live metadata for 8 posts across 2023–2026 (including the oddly small ones): archived sizes match exactly what a logged-in fetch offers (1440×1800, 3277×4096, 2976×3720; 400×400 / 742×928 items are small at the source, sitting next to 1440 px siblings in the same carousel).
- Logged-out gdl does not download smaller files — it fails (login redirect). No logged-out media ever entered the archive.
- Videos: this is where there is something to gain. Of ~1,300 archived videos in the six scraped
profiles, ~1,200 are 720p (min side ≤ 720) and ~100 (2026 reels) are 1080p. A 2026 reel
(
DTpycSlDKHs, archived 720×1280) offers 1080×1920 VP9 video-only DASH + separate AAC audio. A 2024 post (C7RmlCcSWNR) tops out at 1276×720 — same as the archive — so older videos probably gain nothing. Expected beneficiaries: 2025–26 videos, roughly 700 posts. Not verified how far back 1080p exists.
Decisions already made (by the owner)
- Videos in a separate pass from anything else (images need none).
- Keep the replaced files for review in a
_superseded/area — never overwrite/delete. - Output
.webm, not.mp4(video stream copied, audio re-encoded AAC → Opus 96 kbps; the audio stream is ~43 kbps HE-AAC so the loss is negligible). Tested on real hardware: iPhone, iOS 18.7 Safari plays the VP9+Opus.webmat 1080×1920 and fails the VP9+AAC.mp4(MediaErrorcode 4, no video track) — even thoughcanPlayTypereports "probably" for it, so never trustcanPlayTypehere. VP9-in-MP4 (the lossless remux) is therefore out. Re-encoding VP9 → H.264 was rejected (bigger, softer, no gain over the 720p original).
How the sample was produced (reproducible, one post)
# portrait video: 1080 is the WIDTH — `height=1080` selects nothing and fails "format not available"
yt-dlp --cookies-from-browser chrome:/home/matt/.config/google-chrome-devtools \
-f "bv*[width>=1080]+ba" --merge-output-format mp4 -o 'X.%(ext)s' https://www.instagram.com/p/<shortcode>/
ffmpeg -i X.mp4 -c:v copy -c:a libopus -b:a 96k X.webm # ffmpeg 7.1.5 on mattellite
Sizes for that post: 6.46 MB .mp4 / 6.53 MB .webm. For a browser test you need HTTP Range
support (Python's http.server has none; Safari requires it) — the throwaway server used is not kept.
gallery-dl needs $HOME/.local/bin on PATH over plain ssh.
Open decisions / work before building
| item | notes |
|---|---|
| Viewer MIME type | src/lib/archive-cache.ts:127 and src/hooks/useArchiveScanner.ts:272,344,391 mint every video blob URL as video/mp4. Derive the type from the extension (.webm → video/webm) or Safari/iOS may refuse a WebM labelled mp4. MEDIA_RE / isVideo already recognise .webm. |
| Filename / dedup | The archive names YYYY-MM-DD_<user> - <shortcode>[ - <n>].mp4. Swapping to .webm must not leave both in archives/ (the viewer would show duplicates) — the old .mp4 has to be moved out, not just joined by a .webm. Check parseArchiveFilename (src/lib/archive-patterns.ts) for the extension handling and the JD2-vs-gdl same-stem duplicate logic. |
| Skip-archive | artms.db keys on instagram<media_id>, so a normal run will never re-fetch these. The upgrade tool must fetch targeted items deliberately and must not touch/poison artms.db semantics. |
_superseded/ location |
Must be outside /volume1/rslsync/sync/Instagram-archive/archives/ — that tree is scanned by the viewer and inside the Resilio-synced share (it would sync every replaced file to devices). Not decided. Also unchecked: whether agentapi can write outside that share. Use rsync --backup --backup-dir=<dir>/<date>/ so the old copy is kept. |
| Carousel videos | yt-dlp on a carousel post URL fails ("No video formats found") — see DIa5NpWRnIP. gdl's own metadata gives per-child ytdl: URLs for videos in carousels; the tool needs to handle child items, not post URLs. |
| Request budget | One yt-dlp -F-style request per post to learn what 1080p exists (plus the download). ~700 candidates ⇒ suggested ≤ 40 posts/day, resumable, behind check-session.py, hand-started or a slow timer — never a burst. Candidates can be pre-ranked from the archive alone (720p, 2025–26) at zero request cost. |
| Storage | 1080p VP9 ≈ 1.6× the 720p file (6 MB vs ~4 MB for that sample, plus the kept original). Estimate before running at scale. |
Planned design
- Candidate list, zero requests: scan the NAS archive (read-only, ssh
agentapi@<nas>from mattellite; the scans in this doc used header parsing in plain Python over stdin because the NAS has no ffprobe) for videos ≤ 720 min-side from 2025-01 onward, newest first. - Dry-run mode (default): for each candidate, one request to learn the best offered stream,
record
{shortcode, archived WxH, offered WxH}in a state file; print counts + estimated GB. Downloads nothing. - Execute mode: only where offered width > archived width; download video+audio,
ffmpeg -c:v copy -c:a libopus -b:a 96k, verify withffprobe(dimensions, duration within 0.5 s of the original, both streams present), publish the.webm, move the old file to_superseded/. Cap per run (--max-posts), pace with the same 12–20 s gaps, stop on any 429 / login redirect. - First step when resuming: do ONE reel end to end by hand (
DTpycSlDKHs, ~3 requests) to prove download → remux → verify → publish → supersede → viewer playback, then the dry-run, and only after the weekly profiles timer has run cleanly on the new account (first fire ≈ 2026-10-23).
Things measured along the way (for reference)
- Image header scan by year (six scraped profiles): ≥ 1440 px for the vast majority; ~75 images
< 1000 px, all checked samples genuinely small at the source.
official_artms2023–24 has a cluster < 1440 px (~1080-class originals) — not probed. - Video min-side classes (six profiles): 2023 ≤720: 304, 2024 ≤720: 248, 2025 ≤720: 368 (+1 at 1080), 2026 ≤720: 302 and ≤1080: 100 (+1 at 1440×2560). A handful ≤540.
- The scan tools (read-only, stdlib only, run on the NAS user over ssh stdin) were throwaway and are not in the repo; recreate from this description if needed.