feat: read gallery-dl sidecars for reel type and post dates
The .json sidecars published with the ARTMS fetch were inert: the scanner fed them through the Instaloader path, where `node.edge_media_to_caption` and `checkIsStory`'s `product_type` are both absent, so nothing happened. They are now recognised structurally -- flat, with post_shortcode and type, and none of the markers the other two JSON shapes carry -- and used for three things: - `type` sets post.isReel, which post-tabs prefers over every fallback. This is Instagram's own classification and it disagrees with ours a lot: of 781 items in "official_artms - reels", the sidecars say only 360 are reels. The other 421 are feed videos the clips endpoint returns via include_feed_video, and the directory-based rule counted them all. - `description` fills the caption where no .txt exists. - `date` dates a post whose filename could not. Also fixes date precedence. Only JDownloader highlights lack a date in the filename, so parseArchiveFilename now marks those as mtime-derived and the scanner lets any real date replace them -- previously the date depended on which file the scan reached first. Verified against real published files: a directory of three type=post and three type=reel renders 6 in the grid and exactly the 3 reels in the Reels tab. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -98,3 +98,44 @@ describe('postsForTab', () => {
|
||||
expect(postsForTab([post('A')], 'saved')).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Once an archive carries gallery-dl sidecars, the guesswork above is replaced
|
||||
* by Instagram's own classification. These are the cases the heuristic got
|
||||
* wrong (see docs/gallery-dl.md).
|
||||
*/
|
||||
describe('explicit isReel from a sidecar', () => {
|
||||
it('beats the lone-video heuristic for an ordinary feed video', () => {
|
||||
// A single mp4 that Instagram calls a post, not a reel — indistinguishable
|
||||
// by shape alone.
|
||||
const posts = [video('DbdG9L9jU4m', { isReel: false })];
|
||||
expect(postsForTab(posts, 'reels')).toEqual([]);
|
||||
expect(postsForTab(posts, 'posts')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('recognises a reel that lives in the profile grid', () => {
|
||||
// Shared to feed, so it sits in the base directory with source 'posts'.
|
||||
const posts = [post('A'), video('C8FHM6EJl15', { source: 'posts', isReel: true })];
|
||||
expect(postsForTab(posts, 'reels').map(p => p.id)).toEqual(['C8FHM6EJl15']);
|
||||
expect(postsForTab(posts, 'posts')).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('beats the directory when both are present', () => {
|
||||
const posts = [
|
||||
video('u - reels/A', { source: 'reels', isReel: false }),
|
||||
video('u - reels/B', { source: 'reels' }),
|
||||
];
|
||||
// A is a feed video that the reels tab happened to return; B is unlabelled
|
||||
// and falls back to its directory.
|
||||
expect(postsForTab(posts, 'reels').map(p => p.id)).toEqual(['u - reels/B']);
|
||||
});
|
||||
|
||||
it('falls back per post, so a mixed archive still works', () => {
|
||||
const posts = [
|
||||
video('labelled', { isReel: true }),
|
||||
video('unlabelled'),
|
||||
carousel('C'),
|
||||
];
|
||||
expect(postsForTab(posts, 'reels').map(p => p.id)).toEqual(['labelled', 'unlabelled']);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user