docs: design a gallery-dl replacement for the JDownloader fetcher
Every claim in docs/gallery-dl.md was measured against the live site and
the archive rather than taken from documentation, because two of the
assumptions turned out to be wrong.
The safety model is the reason the config looks the way it does.
gallery-dl has two API backends: the graphql one issues a request PER
POST for every video and carousel -- the pattern that got this account
banned via Instaloader -- while the default rest one paginates listings
at 30-50 items and carries carousel_media, video_versions and
product_type inline. A 300-post profile costs ~10 requests.
Findings worth recording:
- JD2 stamped filenames in desktop LOCAL time (US Eastern), not UTC.
Across 212 comparable posts: UTC 19 mismatches, UTC-5 10, UTC-4 zero.
{date:Olocal/%Y-%m-%d} reproduces it; the trailing separator must be
omitted or it lands in the strftime format.
- A profile's reels tab returns collab reels owned by OTHER accounts, so
the directory must be forced with -D. JD2 did the same: chuuo3o and
official_artms filenames sit inside "0ct0ber19 - reels".
- Stories and highlights need per-item {shortcode}; {post_shortcode} is
the reel's id and is shared by every item. {date} is per-item, verified
on a 154-item highlight with distinct times.
- gallery-dl reproduces JD2's caption .txt exactly, including writing
nothing for an empty caption and omitting the trailing newline.
- The json sidecar needs `include`, not `fields`; `fields` silently does
nothing in mode:json and leaks audio_user blobs. It yields `type`
(post/reel) -- Instagram's own flag, which can retire the lone-video
heuristic once the scanner reads it.
Naming differences between the two tools are cosmetic: EXPORT_RE already
makes the index optional and parseInt normalises zero-padding, so a mixed
archive parses identically. Tests pin that down.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -116,3 +116,43 @@ describe('scopedPostId', () => {
|
|||||||
expect(inPosts).not.toBe(inHighlight);
|
expect(inPosts).not.toBe(inHighlight);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gallery-dl is replacing JDownloader as the fetcher (docs/gallery-dl.md).
|
||||||
|
* Its naming differs cosmetically, and these cases pin down that the two
|
||||||
|
* interoperate so a mixed archive parses identically.
|
||||||
|
*/
|
||||||
|
describe('gallery-dl / JDownloader naming interop', () => {
|
||||||
|
it('treats a single-media post the same with or without an index', () => {
|
||||||
|
const jd2 = parseArchiveFilename('2023-04-19_0ct0ber19 - CrORBIcJJbM.mp4')!;
|
||||||
|
const gdl = parseArchiveFilename('2023-04-19_0ct0ber19 - CrORBIcJJbM - 1.mp4')!;
|
||||||
|
expect(jd2.postId).toBe(gdl.postId);
|
||||||
|
expect(jd2.index).toBe(gdl.index);
|
||||||
|
expect(jd2.index).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('normalises zero-padded carousel indices', () => {
|
||||||
|
// JD2 pads to the width of the media count (10+ items -> "01"), and
|
||||||
|
// gallery-dl's count can be one higher, so the same post may be padded
|
||||||
|
// by one tool and not the other.
|
||||||
|
expect(parseArchiveFilename('2024-04-17_0ct0ber19 - C53YPQzp7Wj - 09.jpg')!.index).toBe(9);
|
||||||
|
expect(parseArchiveFilename('2024-04-17_0ct0ber19 - C53YPQzp7Wj - 9.jpg')!.index).toBe(9);
|
||||||
|
expect(parseArchiveFilename('2023-11-03_0ct0ber19 - CzM8Uf6B6H_ - 01.jpg')!.index).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reads a gallery-dl story name, which carries a per-item shortcode', () => {
|
||||||
|
const p = parseArchiveFilename('2026-08-16_official_artms - DcF9OyhBJ1H.jpg', 'stories')!;
|
||||||
|
expect(p.postId).toBe('DcF9OyhBJ1H');
|
||||||
|
expect(p.date).toBe('2026-08-16');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('gives a dated highlight a real date instead of the mtime fallback', () => {
|
||||||
|
const mtime = Date.parse('2026-08-17T00:00:00Z');
|
||||||
|
const undated = parseArchiveFilename('0ct0ber19 - C-IImhvpFuk.jpg', 'highlight', mtime)!;
|
||||||
|
const dated = parseArchiveFilename('2024-08-04_0ct0ber19 - C-IImhvpFuk.jpg', 'highlight', mtime)!;
|
||||||
|
// Same item either way, so re-fetching cannot split it into two posts.
|
||||||
|
expect(dated.postId).toBe(undated.postId);
|
||||||
|
expect(undated.date).toBe('2026-08-17');
|
||||||
|
expect(dated.date).toBe('2024-08-04');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user