Skip to content

Commit

Permalink
fix: add serializer
Browse files Browse the repository at this point in the history
  • Loading branch information
markusahlstrand committed May 13, 2024
1 parent 33cf7da commit d0d47e3
Show file tree
Hide file tree
Showing 4 changed files with 345 additions and 1 deletion.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# podcast-parser

A library for parsing podcast feeds to JSON and the Sesamy format.
A library for parsing podcast feeds to JSON and the Sesamy format. It also provides a function to serizalize a feed to an RSS file.

## Installation

Expand All @@ -15,4 +15,7 @@ const { parseFeedToJson, parseFeedToSesamy } = require('@sesamy/podcast-parser')

const podcastFeed = await parseFeedToJson(podcast_feed_url);
const sesamyPodcastFeed = parseFeedToSesamy(podcastFeed);

// And serialize the feed back to an RSS file
const rssFeed = serializeFeedToRss(podcastFeed);
```
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './feed-parser';
export * from './sesamy-parser';
export * from './serialize-feed';
145 changes: 145 additions & 0 deletions src/serialize-feed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import iso6392BTo1 from './utils/country-codes';
import { Item, Rss, RssBoolean, SesamyFeed, SesamyFeedEpisode } from '@sesamy/podcast-schemas';

function formatDuration(totalSeconds: number): string {
if (!totalSeconds) {
return '';
}

const seconds = (totalSeconds % 60).toString().padStart(2, '0');
const minutes = (Math.floor(totalSeconds / 60) % 60).toString().padStart(2, '0');
const hours = (Math.floor(totalSeconds / 3600) % 60).toString().padStart(2, '0');

return `${hours}:${minutes}:${seconds}`;
}

function boolToString(bool?: boolean): RssBoolean {
return bool ? 'yes' : 'no';
}

function utcDate(date?: string) {
if (!date) {
return undefined;
}

return new Date(Date.parse(date)).toUTCString();
}

function renderItem(episode: SesamyFeedEpisode) {
const item: Item = {
link: episode.link,
guid: {
'@_isPermaLink': false,
'#text': episode.guid,
},
title: episode.title,
description: episode.description,
'content:encoded': episode.descriptionWithHtml,
'itunes:subtitle': episode.description?.slice(0, Math.min(255, episode.description.length)),
'itunes:duration': formatDuration(episode.duration!),
'itunes:episodeType': episode.episodeType || 'full',
'@_permissions': episode.permissions.join('|'),
enclosure: episode.url
? [
{
'@_url': episode.url,
'@_length': episode.contentLength?.toString() || '',
'@_type': 'audio/mpeg',
},
]
: [],
'itunes:episode': episode.episode,
'itunes:season': episode.season,
pubDate: utcDate(episode.publishDate)!,
};

if (episode.image) {
item['itunes:image'] = {
'@_href': episode.image,
};
}

return item;
}

export function generateRssFeed(feed: SesamyFeed): Rss {
const episodes: Item[] = feed.episodes.filter(episode => !episode.isLocked).map(renderItem);

const lockedEpisodes: Item[] = feed.episodes.filter(episode => episode.isLocked).map(renderItem);

const rss: Rss = {
'@_version': '2.0',
'@_xmlns:atom': 'http://www.w3.org/2005/Atom',
'@_xmlns:itunes': 'http://www.itunes.com/dtds/podcast-1.0.dtd',
'@_xmlns:sesamy': 'http://schemas.sesamy.com/feed/1.0',
'@_xmlns:googleplay': 'http://www.google.com/schemas/play-podcasts/1.0',
'@_xmlns:content': 'http://purl.org/rss/1.0/modules/content/',
channel: {
title: feed.titleWithUsername || feed.title,
link: feed.link,
'atom:link': [
{
'@_rel': 'self',
'@_type': 'application/rss+xml',
'@_href': feed.link,
},
],
copyright: 'Sesamy AB',
description: feed.description,
pubDate: utcDate(feed.publishDate),
lastBuildDate: new Date().toUTCString(),
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
language: iso6392BTo1(feed.language!) || 'en',
ttl: 30,
image: {
url: feed.image || '',
title: feed.titleWithUsername || feed.title,
link: feed.link,
},
'googleplay:block': boolToString(feed.sesamy.isPrivate),
'itunes:block': boolToString(feed.sesamy.isPrivate),
'itunes:image': {
'@_href': feed.image || '',
},
'itunes:summary': feed.description,
'itunes:author': feed.author,
'itunes:category': feed.categories.map(category => ({
'@_text': category,
})),
'itunes:owner': {
'itunes:name': feed.owner?.name || '',
'itunes:email': feed.owner?.email || 'feed@sesamy.com',
},
'itunes:explicit': boolToString(feed.isExplicit),
'sesamy:title': feed.title,
'sesamy:private': feed.sesamy.isPrivate.toString(),
'sesamy:vendor-id': feed.sesamy.vendorId,
'sesamy:product': feed.products.map(product => ({
id: product.id,
price: product.price,
currency: product.currency,
type: product.type,
title: product.title,
description: product.description || '',
image: product.image,
purchase_type: product.purchaseType,
period: product.period,
time: product.time,
})),
'sesamy:sesamy-item': lockedEpisodes,
item: episodes,
},
};

if (feed.user) {
console.log('feed.user', feed.user);

rss.channel['sesamy:user'] = {
'sesamy:id': feed.user.id,
'sesamy:name': feed.user.name,
'sesamy:email': feed.user.email,
};
}

return rss;
}
195 changes: 195 additions & 0 deletions src/utils/country-codes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
/**
* Map of ISO 639-2 bibliographic codes (`dut`) to ISO 639-1 codes (`nl`).
*
* @type {Record<string, string>}
*/
const iso6392BTo1Records: Record<string, string> = {
aar: 'aa',
abk: 'ab',
afr: 'af',
aka: 'ak',
alb: 'sq',
amh: 'am',
ara: 'ar',
arg: 'an',
arm: 'hy',
asm: 'as',
ava: 'av',
ave: 'ae',
aym: 'ay',
aze: 'az',
bak: 'ba',
bam: 'bm',
baq: 'eu',
bel: 'be',
ben: 'bn',
bih: 'bh',
bis: 'bi',
bos: 'bs',
bre: 'br',
bul: 'bg',
bur: 'my',
cat: 'ca',
cha: 'ch',
che: 'ce',
chi: 'zh',
chu: 'cu',
chv: 'cv',
cor: 'kw',
cos: 'co',
cre: 'cr',
cze: 'cs',
dan: 'da',
div: 'dv',
dut: 'nl',
dzo: 'dz',
eng: 'en',
epo: 'eo',
est: 'et',
ewe: 'ee',
fao: 'fo',
fij: 'fj',
fin: 'fi',
fre: 'fr',
fry: 'fy',
ful: 'ff',
geo: 'ka',
ger: 'de',
gla: 'gd',
gle: 'ga',
glg: 'gl',
glv: 'gv',
gre: 'el',
grn: 'gn',
guj: 'gu',
hat: 'ht',
hau: 'ha',
heb: 'he',
her: 'hz',
hin: 'hi',
hmo: 'ho',
hrv: 'hr',
hun: 'hu',
ibo: 'ig',
ice: 'is',
ido: 'io',
iii: 'ii',
iku: 'iu',
ile: 'ie',
ina: 'ia',
ind: 'id',
ipk: 'ik',
ita: 'it',
jav: 'jv',
jpn: 'ja',
kal: 'kl',
kan: 'kn',
kas: 'ks',
kau: 'kr',
kaz: 'kk',
khm: 'km',
kik: 'ki',
kin: 'rw',
kir: 'ky',
kom: 'kv',
kon: 'kg',
kor: 'ko',
kua: 'kj',
kur: 'ku',
lao: 'lo',
lat: 'la',
lav: 'lv',
lim: 'li',
lin: 'ln',
lit: 'lt',
ltz: 'lb',
lub: 'lu',
lug: 'lg',
mac: 'mk',
mah: 'mh',
mal: 'ml',
mao: 'mi',
mar: 'mr',
may: 'ms',
mlg: 'mg',
mlt: 'mt',
mon: 'mn',
nau: 'na',
nav: 'nv',
nbl: 'nr',
nde: 'nd',
ndo: 'ng',
nep: 'ne',
nno: 'nn',
nob: 'nb',
nor: 'no',
nya: 'ny',
oci: 'oc',
oji: 'oj',
ori: 'or',
orm: 'om',
oss: 'os',
pan: 'pa',
per: 'fa',
pli: 'pi',
pol: 'pl',
por: 'pt',
pus: 'ps',
que: 'qu',
roh: 'rm',
rum: 'ro',
run: 'rn',
rus: 'ru',
sag: 'sg',
san: 'sa',
sin: 'si',
slo: 'sk',
slv: 'sl',
sme: 'se',
smo: 'sm',
sna: 'sn',
snd: 'sd',
som: 'so',
sot: 'st',
spa: 'es',
srd: 'sc',
srp: 'sr',
ssw: 'ss',
sun: 'su',
swa: 'sw',
swe: 'sv',
tah: 'ty',
tam: 'ta',
tat: 'tt',
tel: 'te',
tgk: 'tg',
tgl: 'tl',
tha: 'th',
tib: 'bo',
tir: 'ti',
ton: 'to',
tsn: 'tn',
tso: 'ts',
tuk: 'tk',
tur: 'tr',
twi: 'tw',
uig: 'ug',
ukr: 'uk',
urd: 'ur',
uzb: 'uz',
ven: 've',
vie: 'vi',
vol: 'vo',
wel: 'cy',
wln: 'wa',
wol: 'wo',
xho: 'xh',
yid: 'yi',
yor: 'yo',
zha: 'za',
zul: 'zu',
};

export default function iso6392BTo1(iso6392: string): string | undefined {
return iso6392BTo1Records[iso6392];
}

0 comments on commit d0d47e3

Please sign in to comment.