2021-07-16 14:24:42 +02:00
|
|
|
const { generateDownloadUrl } = require('../../ui/util/web');
|
2021-07-01 03:24:02 +02:00
|
|
|
const { URL, SITE_NAME, LBRY_WEB_API } = require('../../config.js');
|
|
|
|
const { Lbry } = require('lbry-redux');
|
2021-06-30 08:02:09 +02:00
|
|
|
const Feed = require('feed').Feed;
|
|
|
|
|
2021-07-01 03:24:02 +02:00
|
|
|
const SDK_API_PATH = `${LBRY_WEB_API}/api/v1`;
|
|
|
|
const proxyURL = `${SDK_API_PATH}/proxy`;
|
|
|
|
Lbry.setDaemonConnectionString(proxyURL);
|
2021-06-30 08:02:09 +02:00
|
|
|
|
2021-07-09 10:06:28 +02:00
|
|
|
const NUM_ENTRIES = 500;
|
|
|
|
|
2021-07-01 03:24:02 +02:00
|
|
|
async function doClaimSearch(options) {
|
|
|
|
let results;
|
|
|
|
try {
|
|
|
|
results = await Lbry.claim_search(options);
|
|
|
|
} catch {}
|
|
|
|
return results ? results.items : undefined;
|
|
|
|
}
|
|
|
|
|
2021-07-09 11:33:34 +02:00
|
|
|
async function getChannelClaim(name, claimId) {
|
|
|
|
let claim;
|
|
|
|
try {
|
|
|
|
const url = `lbry://${name}#${claimId}`;
|
|
|
|
const response = await Lbry.resolve({ urls: [url] });
|
2021-07-01 03:24:02 +02:00
|
|
|
|
2021-07-09 11:33:34 +02:00
|
|
|
if (response && response[url] && !response[url].error) {
|
|
|
|
claim = response && response[url];
|
|
|
|
}
|
|
|
|
} catch {}
|
|
|
|
return claim || 'The RSS URL is invalid or is not associated with any channel.';
|
2021-07-01 03:24:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
async function getClaimsFromChannel(claimId, count) {
|
|
|
|
const options = {
|
|
|
|
channel_ids: [claimId],
|
|
|
|
page_size: count,
|
|
|
|
has_source: true,
|
|
|
|
claim_type: 'stream',
|
|
|
|
order_by: ['creation_timestamp'],
|
|
|
|
no_totals: true,
|
|
|
|
};
|
|
|
|
|
|
|
|
return await doClaimSearch(options);
|
2021-06-30 08:02:09 +02:00
|
|
|
}
|
|
|
|
|
2021-07-09 11:57:59 +02:00
|
|
|
async function getFeed(channelClaim, feedLink) {
|
|
|
|
const replaceLineFeeds = (str) => str.replace(/(?:\r\n|\r|\n)/g, '<br />');
|
2021-07-16 14:24:42 +02:00
|
|
|
|
2021-07-09 11:57:59 +02:00
|
|
|
const fmtDescription = (description) => replaceLineFeeds(description);
|
2021-07-16 14:24:42 +02:00
|
|
|
|
2021-07-09 11:57:59 +02:00
|
|
|
const sanitizeThumbsUrl = (url) => {
|
|
|
|
if (typeof url === 'string' && url.startsWith('https://')) {
|
|
|
|
return encodeURI(url).replace(/&/g, '%26');
|
|
|
|
}
|
|
|
|
return '';
|
|
|
|
};
|
2021-06-30 08:02:09 +02:00
|
|
|
|
2021-07-16 14:24:42 +02:00
|
|
|
const getEnclosure = (claim) => {
|
|
|
|
const value = claim.value;
|
|
|
|
if (!value || !value.stream_type || !value.source || !value.source.media_type) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (value.stream_type) {
|
|
|
|
case 'video':
|
|
|
|
case 'audio':
|
|
|
|
case 'image':
|
|
|
|
case 'document':
|
|
|
|
case 'software':
|
|
|
|
return {
|
|
|
|
url: encodeURI(generateDownloadUrl(claim.name, claim.claim_id)),
|
|
|
|
type: value.source.media_type,
|
|
|
|
length: value.source.size || 0, // Per spec, 0 is a valid fallback.
|
|
|
|
};
|
|
|
|
|
|
|
|
default:
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-07-01 03:24:02 +02:00
|
|
|
const value = channelClaim.value;
|
|
|
|
const title = value ? value.title : channelClaim.name;
|
|
|
|
|
2021-06-30 08:02:09 +02:00
|
|
|
const options = {
|
|
|
|
favicon: URL + '/public/favicon.png',
|
|
|
|
generator: SITE_NAME + ' RSS Feed',
|
2021-07-09 11:57:59 +02:00
|
|
|
title: title + ' on ' + SITE_NAME,
|
|
|
|
description: fmtDescription(value && value.description ? value.description : ''),
|
|
|
|
link: encodeURI(`${URL}/${channelClaim.name}:${channelClaim.claim_id}`),
|
|
|
|
image: sanitizeThumbsUrl(value && value.thumbnail ? value.thumbnail.url : ''),
|
|
|
|
feedLinks: {
|
|
|
|
rss: encodeURI(feedLink),
|
|
|
|
},
|
2021-06-30 08:02:09 +02:00
|
|
|
author: {
|
2021-07-09 11:57:59 +02:00
|
|
|
name: encodeURI(channelClaim.name),
|
|
|
|
link: encodeURI(URL + '/' + channelClaim.name + ':' + channelClaim.claim_id),
|
2021-06-30 08:02:09 +02:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
const feed = new Feed(options);
|
2021-07-09 10:06:28 +02:00
|
|
|
const latestClaims = await getClaimsFromChannel(channelClaim.claim_id, NUM_ENTRIES);
|
2021-06-30 08:02:09 +02:00
|
|
|
|
|
|
|
latestClaims.forEach((c) => {
|
2021-07-01 03:24:02 +02:00
|
|
|
const meta = c.meta;
|
|
|
|
const value = c.value;
|
|
|
|
|
2021-07-16 14:24:42 +02:00
|
|
|
const title = value && value.title ? value.title : c.name;
|
|
|
|
const thumbnailUrl = value && value.thumbnail ? value.thumbnail.url : '';
|
|
|
|
const thumbnailHtml = thumbnailUrl ? `<p><img src="${thumbnailUrl}" alt="thumbnail" title="${title}" /></p>` : '';
|
|
|
|
|
2021-06-30 08:02:09 +02:00
|
|
|
feed.addItem({
|
|
|
|
id: c.claim_id,
|
2021-07-09 11:57:59 +02:00
|
|
|
guid: encodeURI(URL + '/' + c.name + ':' + c.claim_id),
|
|
|
|
title: value && value.title ? value.title : c.name,
|
2021-07-16 14:24:42 +02:00
|
|
|
description: thumbnailHtml + fmtDescription(value && value.description ? value.description : ''),
|
2021-07-09 11:57:59 +02:00
|
|
|
link: encodeURI(URL + '/' + c.name + ':' + c.claim_id),
|
2021-07-01 03:24:02 +02:00
|
|
|
date: new Date(meta ? meta.creation_timestamp * 1000 : null),
|
2021-07-16 14:24:42 +02:00
|
|
|
enclosure: getEnclosure(c),
|
2021-06-30 08:02:09 +02:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
return feed;
|
|
|
|
}
|
|
|
|
|
2021-07-09 11:57:59 +02:00
|
|
|
function postProcess(feed) {
|
|
|
|
// Handle 'Feed' creating an invalid MIME type when trying to guess
|
|
|
|
// from 'https://thumbnails.lbry.com/UCgQ8eREJzR1dO' style of URLs.
|
|
|
|
return feed.replace(/type="image\/\/.*"\/>/g, 'type="image/*"/>');
|
|
|
|
}
|
|
|
|
|
2021-06-30 08:02:09 +02:00
|
|
|
async function getRss(ctx) {
|
|
|
|
if (!ctx.params.claimName || !ctx.params.claimId) {
|
|
|
|
return 'Invalid URL';
|
|
|
|
}
|
|
|
|
|
2021-07-09 11:33:34 +02:00
|
|
|
const channelClaim = await getChannelClaim(ctx.params.claimName, ctx.params.claimId);
|
2021-07-01 03:24:02 +02:00
|
|
|
if (typeof channelClaim === 'string' || !channelClaim) {
|
|
|
|
return channelClaim;
|
2021-06-30 08:02:09 +02:00
|
|
|
}
|
|
|
|
|
2021-07-12 18:17:04 +02:00
|
|
|
const feed = await getFeed(channelClaim, `${URL}${ctx.request.url}`);
|
2021-07-09 11:57:59 +02:00
|
|
|
return postProcess(feed.rss2());
|
2021-06-30 08:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = { getRss };
|