67 lines
2.8 KiB
JavaScript
67 lines
2.8 KiB
JavaScript
'use strict';
|
|
|
|
// reddit gallery images: `s.u` (still) or `s.gif`/`s.mp4` (animated) - URLs come back
|
|
// HTML-entity-encoded (e.g. '&' instead of '&')
|
|
function resolveMediaUrl(media) {
|
|
const url = media?.s?.u || media?.s?.gif || media?.s?.mp4;
|
|
|
|
return url ? url.replace(/&/g, '&') : null;
|
|
}
|
|
|
|
// reddit sends the non-standard 'image/jpg' instead of the real IANA 'image/jpeg' for
|
|
// gallery items - left as-is, this silently breaks anything matching against real mime
|
|
// types downstream (interpolate.js's extension lookup ends up with a '.false' extension,
|
|
// fetch/content.js's addMeta() skips embedding metadata since it only matches 'image/jpeg')
|
|
function normalizeType(type) {
|
|
return type === 'image/jpg' ? 'image/jpeg' : type;
|
|
}
|
|
|
|
async function redditAlbum(host, post, { reddit }) {
|
|
// reddit's submission listing already embeds media_metadata/gallery_data for gallery posts,
|
|
// and curate/posts.js carries them through as post.mediaMetadata/post.galleryData - reuse
|
|
// those instead of re-fetching the same post individually through reddit's own client
|
|
// (reservoir: 6/min, maxConcurrent: 1 - see src/reddit/client.js), which would otherwise cost
|
|
// one full throttled round trip per gallery post. Only fall back to a fresh fetch if the
|
|
// listing didn't have it for some reason (e.g. gallery_data null on the listing too).
|
|
const data = post.mediaMetadata
|
|
? { media_metadata: post.mediaMetadata, gallery_data: post.galleryData }
|
|
: await reddit.getSubmission(post.id);
|
|
const mediaMetadata = data.media_metadata || {};
|
|
|
|
// gallery_data (the ordered {media_id, is_deleted, ...} list for this gallery) has been
|
|
// seen coming back null on a freshly-posted gallery even though media_metadata itself
|
|
// was already fully populated - fall back to media_metadata's own keys, in the order the
|
|
// JSON provided them (reddit sends them in gallery order, and object key order is
|
|
// preserved through JSON.parse), skipping anything not (yet, or any longer) valid
|
|
const galleryItems = data.gallery_data?.items
|
|
|| Object.entries(mediaMetadata)
|
|
.filter(([, media]) => media.status === 'valid')
|
|
.map(([mediaId]) => ({ media_id: mediaId }));
|
|
|
|
const items = galleryItems
|
|
.filter((item) => !item.is_deleted)
|
|
.map((item) => {
|
|
const media = mediaMetadata[item.media_id];
|
|
const url = resolveMediaUrl(media);
|
|
|
|
return url && {
|
|
id: item.media_id,
|
|
url,
|
|
datetime: post.datetime,
|
|
type: normalizeType(media.m) || 'image/jpeg',
|
|
};
|
|
})
|
|
.filter(Boolean);
|
|
|
|
return {
|
|
album: {
|
|
id: host.id,
|
|
url: host.url,
|
|
title: post.title,
|
|
},
|
|
items,
|
|
};
|
|
}
|
|
|
|
module.exports = redditAlbum;
|