RSS: parallel-fetch the stream urls

## Issue
Users are seeing timeout with the RSS calls.
But oddly, odysee.com is significantly slower than the dev instances.
This commit is contained in:
infinite-persistence 2022-05-27 14:23:22 +08:00 committed by Thomas Zarebczan
parent 7504cf07b3
commit b8cf1a6c4c

View file

@ -62,37 +62,54 @@ function encodeWithSpecialCharEncode(string) {
return encodeURIComponent(string).replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29');
}
async function generateEnclosureForClaimContent(claim) {
/**
* Returns an array of stream-url promise results corresponding to the same
* order as the given 'claims' array, or null if there is an error.
*
* Clients must check the promise 'status' for each entry, as some could be a
* failed fetch (i.e. 'rejected').
*
* @param claims Array of freaking claims.
* @returns {Array<{status, value}> | null}
*/
async function fetchStreamUrls(claims) {
return Promise.allSettled(claims.map((c) => fetchStreamUrl(c.name, c.claim_id)))
.then((results) => results)
.catch(() => null);
}
async function generateEnclosureForClaimContent(claim, streamUrl) {
const value = claim.value;
if (!value || !value.stream_type) {
return undefined;
}
const fileExt = value.source && value.source.media_type && '.' + Mime.extension(value.source.media_type);
switch (value.stream_type) {
case 'video':
return {
url: (await fetchStreamUrl(claim.name, claim.claim_id)).replace('/v4/', '/v3/') + (fileExt || '.mp4'), // v3 = mp4 always, v4 may redirect to m3u8
url: streamUrl.replace('/v4/', '/v3/') + (fileExt || '.mp4'), // v3 = mp4 always, v4 may redirect to m3u8
type: (value.source && value.source.media_type) || 'video/mp4',
size: (value.source && value.source.size) || 0, // Per spec, 0 is a valid fallback.
};
case 'audio':
return {
url: (await fetchStreamUrl(claim.name, claim.claim_id)) + ((fileExt === '.mpga' ? '.mp3' : fileExt) || '.mp3'),
url: streamUrl + ((fileExt === '.mpga' ? '.mp3' : fileExt) || '.mp3'),
type: (value.source && value.source.media_type) || 'audio/mpeg',
size: (value.source && value.source.size) || 0, // Per spec, 0 is a valid fallback.
};
case 'image':
return {
url: (await fetchStreamUrl(claim.name, claim.claim_id)) + (fileExt || '.jpeg'),
url: streamUrl + (fileExt || '.jpeg'),
type: (value.source && value.source.media_type) || 'image/jpeg',
size: (value.source && value.source.size) || 0, // Per spec, 0 is a valid fallback.
};
case 'document':
case 'software':
return {
url: await fetchStreamUrl(claim.name, claim.claim_id),
url: streamUrl,
type: (value.source && value.source.media_type) || undefined,
size: (value.source && value.source.size) || 0, // Per spec, 0 is a valid fallback.
};
@ -174,7 +191,7 @@ const getItunesCategory = (claim) => {
// "Note: Although you can specify more than one category and subcategory
// in your RSS feed, Apple Podcasts only recognizes the first category and
// subcategory."
// --> The only parse the first found tag.
// --> They only parse the first found tag.
return itunesCategory.replace('&', '&amp;');
}
}
@ -244,6 +261,9 @@ async function generateFeed(feedLink, channelClaim, claimsInChannel) {
],
});
// --- Parallel pre-fetch of stream url ---
const streamUrls = await fetchStreamUrls(claimsInChannel);
// --- Content ---
for (let i = 0; i < claimsInChannel.length; ++i) {
const c = claimsInChannel[i];
@ -258,6 +278,9 @@ async function generateFeed(feedLink, channelClaim, claimsInChannel) {
const date =
c.value && c.value.release_time ? c.value.release_time * 1000 : c.meta && c.meta.creation_timestamp * 1000;
const claimStreamUrl = streamUrls ? streamUrls[i] : '';
const streamUrl = claimStreamUrl.status === 'fulfilled' ? claimStreamUrl.value : '';
feed.item({
title: title,
description: description,
@ -265,7 +288,7 @@ async function generateFeed(feedLink, channelClaim, claimsInChannel) {
guid: undefined, // defaults to 'url'
author: undefined, // defaults feed author property
date: new Date(date),
enclosure: await generateEnclosureForClaimContent(c),
enclosure: await generateEnclosureForClaimContent(c, streamUrl),
custom_elements: [
{ 'itunes:title': title },
{ 'itunes:author': channelTitle },