rss tests
Various fixes and improvements to work with Apple Podcasts f f image support
This commit is contained in:
parent
7921c0971e
commit
6db75f8a66
2 changed files with 50 additions and 16 deletions
|
@ -3,7 +3,10 @@ const { URL, LBRY_WEB_STREAMING_API } = require('../../config');
|
||||||
const CONTINENT_COOKIE = 'continent';
|
const CONTINENT_COOKIE = 'continent';
|
||||||
|
|
||||||
function generateStreamUrl(claimName, claimId) {
|
function generateStreamUrl(claimName, claimId) {
|
||||||
return `${LBRY_WEB_STREAMING_API}/content/claims/${claimName}/${claimId}/stream`;
|
return `${LBRY_WEB_STREAMING_API}/content/claims/${encodeURIComponent(claimName)
|
||||||
|
.replace(/'/g, '%27')
|
||||||
|
.replace(/\(/g, '%28')
|
||||||
|
.replace(/\)/g, '%29')}/${claimId}/stream`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateEmbedUrl(claimName, claimId, includeStartTime, startTime, referralLink) {
|
function generateEmbedUrl(claimName, claimId, includeStartTime, startTime, referralLink) {
|
||||||
|
@ -16,7 +19,10 @@ function generateEmbedUrl(claimName, claimId, includeStartTime, startTime, refer
|
||||||
urlParams.append('r', referralLink);
|
urlParams.append('r', referralLink);
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${URL}/$/embed/${encodeURIComponent(claimName).replace(/'/g, '%27')}/${claimId}?${urlParams.toString()}`;
|
return `${URL}/$/embed/${encodeURIComponent(claimName)
|
||||||
|
.replace(/'/g, '%27')
|
||||||
|
.replace(/\(/g, '%28')
|
||||||
|
.replace(/\)/g, '%29')}/${claimId}?${urlParams.toString()}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateDownloadUrl(claimName, claimId) {
|
function generateDownloadUrl(claimName, claimId) {
|
||||||
|
|
|
@ -2,6 +2,7 @@ const { generateStreamUrl } = require('../../ui/util/web');
|
||||||
const { URL, SITE_NAME, LBRY_WEB_API } = require('../../config.js');
|
const { URL, SITE_NAME, LBRY_WEB_API } = require('../../config.js');
|
||||||
const { Lbry } = require('lbry-redux');
|
const { Lbry } = require('lbry-redux');
|
||||||
const Rss = require('rss');
|
const Rss = require('rss');
|
||||||
|
const Mime = require('mime-types');
|
||||||
|
|
||||||
const SDK_API_PATH = `${LBRY_WEB_API}/api/v1`;
|
const SDK_API_PATH = `${LBRY_WEB_API}/api/v1`;
|
||||||
const proxyURL = `${SDK_API_PATH}/proxy`;
|
const proxyURL = `${SDK_API_PATH}/proxy`;
|
||||||
|
@ -46,7 +47,7 @@ async function getClaimsFromChannel(claimId, count) {
|
||||||
page_size: count,
|
page_size: count,
|
||||||
has_source: true,
|
has_source: true,
|
||||||
claim_type: 'stream',
|
claim_type: 'stream',
|
||||||
order_by: ['creation_timestamp'],
|
order_by: ['release_time'],
|
||||||
no_totals: true,
|
no_totals: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -57,16 +58,39 @@ async function getClaimsFromChannel(claimId, count) {
|
||||||
// Helpers
|
// Helpers
|
||||||
// ****************************************************************************
|
// ****************************************************************************
|
||||||
|
|
||||||
|
function encodeWithSpecialCharEncode(string) {
|
||||||
|
// encodeURIComponent doesn't encode `'` and others
|
||||||
|
// which other services may not like
|
||||||
|
return encodeURIComponent(string).replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29');
|
||||||
|
}
|
||||||
|
|
||||||
const generateEnclosureForClaimContent = (claim) => {
|
const generateEnclosureForClaimContent = (claim) => {
|
||||||
const value = claim.value;
|
const value = claim.value;
|
||||||
if (!value || !value.stream_type) {
|
if (!value || !value.stream_type) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
const fileExt = value.source && value.source.media_type && '.' + Mime.extension(value.source.media_type);
|
||||||
|
|
||||||
switch (value.stream_type) {
|
switch (value.stream_type) {
|
||||||
case 'video':
|
case 'video':
|
||||||
|
return {
|
||||||
|
url: generateStreamUrl(claim.name, claim.claim_id) + (fileExt || '.mp4'),
|
||||||
|
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':
|
case 'audio':
|
||||||
|
return {
|
||||||
|
url: generateStreamUrl(claim.name, claim.claim_id) + (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':
|
case 'image':
|
||||||
|
return {
|
||||||
|
url: generateStreamUrl(claim.name, claim.claim_id) + (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 'document':
|
||||||
case 'software':
|
case 'software':
|
||||||
return {
|
return {
|
||||||
|
@ -102,11 +126,10 @@ const isEmailRoughlyValid = (email) => /^\S+@\S+$/.test(email);
|
||||||
* @returns any
|
* @returns any
|
||||||
*/
|
*/
|
||||||
const generateItunesOwnerElement = (claim) => {
|
const generateItunesOwnerElement = (claim) => {
|
||||||
let name = '---';
|
|
||||||
let email = 'no-reply@odysee.com';
|
let email = 'no-reply@odysee.com';
|
||||||
|
let name = claim && (claim.value && claim.value.title ? claim.value.title : claim.name);
|
||||||
|
|
||||||
if (claim && claim.value) {
|
if (claim && claim.value) {
|
||||||
name = claim.name;
|
|
||||||
if (isEmailRoughlyValid(claim.value.email)) {
|
if (isEmailRoughlyValid(claim.value.email)) {
|
||||||
email = claim.value.email;
|
email = claim.value.email;
|
||||||
}
|
}
|
||||||
|
@ -118,7 +141,7 @@ const generateItunesOwnerElement = (claim) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateItunesExplicitElement = (claim) => {
|
const generateItunesExplicitElement = (claim) => {
|
||||||
const tags = (claim && claim.value && claim.tags) || [];
|
const tags = (claim && claim.value && claim.value.tags) || [];
|
||||||
return { 'itunes:explicit': tags.includes('mature') ? 'yes' : 'no' };
|
return { 'itunes:explicit': tags.includes('mature') ? 'yes' : 'no' };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -145,15 +168,16 @@ const getItunesCategory = (claim) => {
|
||||||
'TV & Film',
|
'TV & Film',
|
||||||
];
|
];
|
||||||
|
|
||||||
const tags = (claim && claim.value && claim.tags) || [];
|
const tags = (claim && claim.value && claim.value.tags) || [];
|
||||||
for (let i = 0; i < tags.length; ++i) {
|
|
||||||
const tag = tags[i];
|
for (let i = 0; i < itunesCategories.length; ++i) {
|
||||||
if (itunesCategories.includes(tag)) {
|
const itunesCategory = itunesCategories[i];
|
||||||
|
if (tags.includes(itunesCategory.toLowerCase())) {
|
||||||
// "Note: Although you can specify more than one category and subcategory
|
// "Note: Although you can specify more than one category and subcategory
|
||||||
// in your RSS feed, Apple Podcasts only recognizes the first category and
|
// in your RSS feed, Apple Podcasts only recognizes the first category and
|
||||||
// subcategory."
|
// subcategory."
|
||||||
// --> The only parse the first found tag.
|
// --> The only parse the first found tag.
|
||||||
return tag.replace('&', '&');
|
return itunesCategory.replace('&', '&');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,8 +221,9 @@ const getFormattedDescription = (claim) => {
|
||||||
|
|
||||||
function generateFeed(feedLink, channelClaim, claimsInChannel) {
|
function generateFeed(feedLink, channelClaim, claimsInChannel) {
|
||||||
// --- Channel ---
|
// --- Channel ---
|
||||||
|
let channelTitle = (channelClaim.value && channelClaim.value.title) || channelClaim.name;
|
||||||
const feed = new Rss({
|
const feed = new Rss({
|
||||||
title: ((channelClaim.value && channelClaim.value.title) || channelClaim.name) + ' on ' + SITE_NAME,
|
title: channelTitle + ' on ' + SITE_NAME,
|
||||||
description: getFormattedDescription(channelClaim),
|
description: getFormattedDescription(channelClaim),
|
||||||
feed_url: feedLink,
|
feed_url: feedLink,
|
||||||
site_url: URL,
|
site_url: URL,
|
||||||
|
@ -206,7 +231,7 @@ function generateFeed(feedLink, channelClaim, claimsInChannel) {
|
||||||
language: getLanguageValue(channelClaim),
|
language: getLanguageValue(channelClaim),
|
||||||
custom_namespaces: { itunes: 'http://www.itunes.com/dtds/podcast-1.0.dtd' },
|
custom_namespaces: { itunes: 'http://www.itunes.com/dtds/podcast-1.0.dtd' },
|
||||||
custom_elements: [
|
custom_elements: [
|
||||||
{ 'itunes:author': channelClaim.name },
|
{ 'itunes:author': channelTitle },
|
||||||
{
|
{
|
||||||
'itunes:category': [
|
'itunes:category': [
|
||||||
{
|
{
|
||||||
|
@ -231,17 +256,20 @@ function generateFeed(feedLink, channelClaim, claimsInChannel) {
|
||||||
: '';
|
: '';
|
||||||
const description = thumbnailHtml + getFormattedDescription(c);
|
const description = thumbnailHtml + getFormattedDescription(c);
|
||||||
|
|
||||||
|
const url = `${URL}/${encodeWithSpecialCharEncode(c.name)}:${c.claim_id}`;
|
||||||
|
const date = c.release_time ? c.release_time * 1000 : c.meta && c.meta.creation_timestamp * 1000;
|
||||||
|
|
||||||
feed.item({
|
feed.item({
|
||||||
title: title,
|
title: title,
|
||||||
description: description,
|
description: description,
|
||||||
url: `${URL}/${c.name}:${c.claim_id}`,
|
url: url,
|
||||||
guid: undefined, // defaults to 'url'
|
guid: undefined, // defaults to 'url'
|
||||||
author: undefined, // defaults feed author property
|
author: undefined, // defaults feed author property
|
||||||
date: new Date(c.meta ? c.meta.creation_timestamp * 1000 : null),
|
date: new Date(date),
|
||||||
enclosure: generateEnclosureForClaimContent(c),
|
enclosure: generateEnclosureForClaimContent(c),
|
||||||
custom_elements: [
|
custom_elements: [
|
||||||
{ 'itunes:title': title },
|
{ 'itunes:title': title },
|
||||||
{ 'itunes:author': channelClaim.name },
|
{ 'itunes:author': channelTitle },
|
||||||
generateItunesImageElement(c),
|
generateItunesImageElement(c),
|
||||||
generateItunesDurationElement(c),
|
generateItunesDurationElement(c),
|
||||||
generateItunesExplicitElement(c),
|
generateItunesExplicitElement(c),
|
||||||
|
|
Loading…
Reference in a new issue