pfeed
Version:
Feed is a RSS 2.0, Atom 1.0 and Json Feed 1.0 generator for Node.js, based on work from Jean-Philippe Monette and initiated by a need of the PeerTube project
776 lines (643 loc) • 23.8 kB
JavaScript
"use strict";
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _xml = require("xml");
var _xml2 = _interopRequireDefault(_xml);
var _has = require("lodash/has");
var _has2 = _interopRequireDefault(_has);
var _pick = require("lodash/pick");
var _pick2 = _interopRequireDefault(_pick);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var GENERATOR = "Feed for Node.js";
var DOCTYPE = '<?xml version="1.0" encoding="utf-8"?>\n';
var Feed = function () {
function Feed(options) {
_classCallCheck(this, Feed);
this.options = options;
this.items = [];
this.categories = [];
this.contributors = [];
this.extensions = [];
this.custom_fields = [];
}
_createClass(Feed, [{
key: "addItem",
value: function addItem(item) {
this.items.push(item);
}
}, {
key: "addCustomField",
value: function addCustomField(field_name) {
this.custom_fields.push(field_name);
}
}, {
key: "addCategory",
value: function addCategory(category) {
this.categories.push(category);
}
}, {
key: "addContributor",
value: function addContributor(contributor) {
this.contributors.push(contributor);
}
}, {
key: "addExtension",
value: function addExtension(extension) {
this.extensions.push(extension);
}
}, {
key: "render",
value: function render(format) {
console.warn("DEPRECATED: use atom1() or rss2() instead of render()");
if (format === "atom-1.0") {
return this.atom1();
} else {
return this.rss2();
}
}
}, {
key: "atom1",
value: function atom1() {
var _this = this;
var options = this.options;
var feed = [{ _attr: { xmlns: "http://www.w3.org/2005/Atom" } }, { id: options.id }, { title: options.title }, {
updated: options.updated ? this.ISODateString(options.updated) : this.ISODateString(new Date())
}, { generator: options.generator || GENERATOR }];
var root = [{ feed: feed }];
if (options.author) {
var _options$author = options.author,
name = _options$author.name,
email = _options$author.email,
link = _options$author.link;
var author = [];
if (name) {
author.push({ name: name });
}
if (email) {
author.push({ email: email });
}
if (link) {
author.push({ uri: link });
}
feed.push({ author: author });
}
// link (rel="alternate")
if (options.link) {
feed.push({ link: { _attr: { rel: "alternate", href: options.link } } });
}
// link (rel="self")
var atomLink = options.feed || options.feedLinks && options.feedLinks.atom;
if (atomLink) {
feed.push({ link: { _attr: { rel: "self", href: atomLink } } });
}
// link (rel="hub")
if (options.hub) {
feed.push({ link: { _attr: { rel: "hub", href: options.hub } } });
}
/**************************************************************************
* "feed" node: optional elements
*************************************************************************/
if (options.description) {
feed.push({ subtitle: options.description });
}
if (options.image) {
feed.push({ logo: options.image });
}
if (options.favicon) {
feed.push({ icon: options.favicon });
}
if (options.copyright) {
feed.push({ rights: options.copyright });
}
this.categories.forEach(function (category) {
feed.push({ category: [{ _attr: { term: category } }] });
});
this.contributors.forEach(function (_item) {
var name = _item.name,
email = _item.email,
link = _item.link;
var contributor = [];
if (name) {
contributor.push({ name: name });
}
if (email) {
contributor.push({ email: email });
}
if (link) {
contributor.push({ uri: link });
}
feed.push({ contributor: contributor });
});
// icon
/**************************************************************************
* "entry" nodes
*************************************************************************/
this.items.forEach(function (item) {
//
// entry: required elements
//
var entry = [{ title: { _attr: { type: "html" }, _cdata: item.title } }, { id: item.id || item.link }, { link: [{ _attr: { href: item.link } }] }, { updated: _this.ISODateString(item.date) }];
//
// entry: recommended elements
//
if (item.description) {
entry.push({
summary: { _attr: { type: "html" }, _cdata: item.description }
});
}
if (item.content) {
entry.push({
content: { _attr: { type: "html" }, _cdata: item.content }
});
}
// entry author(s)
if (Array.isArray(item.author)) {
item.author.forEach(function (oneAuthor) {
var name = oneAuthor.name,
email = oneAuthor.email,
link = oneAuthor.link;
var author = [];
if (name) {
author.push({ name: name });
}
if (email) {
author.push({ email: email });
}
if (link) {
author.push({ uri: link });
}
entry.push({ author: author });
});
}
// content
// link - relative link to article
//
// entry: optional elements
//
// category
// contributor
if (Array.isArray(item.contributor)) {
item.contributor.forEach(function (item) {
var name = item.name,
email = item.email,
link = item.link;
var contributor = [];
if (name) {
contributor.push({ name: name });
}
if (email) {
contributor.push({ email: email });
}
if (link) {
contributor.push({ uri: link });
}
entry.push({ contributor: contributor });
});
}
// published
if (item.published) {
entry.push({ published: _this.ISODateString(item.published) });
}
// source
// rights
if (item.copyright) {
entry.push({ rights: item.copyright });
}
feed.push({ entry: entry });
});
return DOCTYPE + (0, _xml2.default)(root, true);
}
}, {
key: "rss2",
value: function rss2() {
var _this2 = this;
var options = this.options;
var isAtom = false;
var isContent = false;
var channel = [{ title: options.title }, { link: options.link }, { description: options.description }, {
lastBuildDate: options.updated ? options.updated.toUTCString() : new Date().toUTCString()
}, { docs: "http://blogs.law.harvard.edu/tech/rss" }, { generator: options.generator || GENERATOR }];
var rss = [{ _attr: { version: "2.0" } }, { channel: channel }];
var root = [{ rss: rss }];
/**
* Channel Image
* http://cyber.law.harvard.edu/rss/rss.html#ltimagegtSubelementOfLtchannelgt
*/
if (options.image) {
channel.push({
image: [{ title: options.title }, { url: options.image }, { link: options.link }]
});
}
/**
* Channel Copyright
* http://cyber.law.harvard.edu/rss/rss.html#optionalChannelElements
*/
if (options.copyright) {
channel.push({ copyright: options.copyright });
}
/**
* Channel Categories
* http://cyber.law.harvard.edu/rss/rss.html#comments
*/
this.categories.forEach(function (category) {
channel.push({ category: category });
});
/**
* Feed URL
* http://validator.w3.org/feed/docs/warning/MissingAtomSelfLink.html
*/
var atomLink = options.feed || options.feedLinks && options.feedLinks.atom;
if (atomLink) {
isAtom = true;
channel.push({
"atom:link": {
_attr: {
href: atomLink,
rel: "self",
type: "application/rss+xml"
}
}
});
}
/**
* Hub for PubSubHubbub
* https://code.google.com/p/pubsubhubbub/
*/
if (options.hub) {
isAtom = true;
channel.push({
"atom:link": {
_attr: {
href: options.hub,
rel: "hub"
}
}
});
}
/**
* Channel Categories
* http://cyber.law.harvard.edu/rss/rss.html#hrelementsOfLtitemgt
*/
this.items.forEach(function (entry) {
var item = [];
// Handle custom fields
_this2.custom_fields.forEach(function (field_name) {
if (entry[field_name]) {
item.push(_defineProperty({}, field_name, entry[field_name]));
}
});
if (entry.title) {
item.push({ title: { _cdata: entry.title } });
}
if (entry.link) {
item.push({ link: entry.link });
}
if (entry.guid) {
if (entry.guid.indexOf("http") === -1) {
item.push({
guid: { _cdata: entry.guid, _attr: { isPermaLink: "false" } }
});
} else {
item.push({ guid: entry.guid });
}
} else if (entry.link) {
item.push({ guid: entry.link });
}
if (entry.date) {
item.push({ pubDate: entry.date.toUTCString() });
}
if (entry.description) {
item.push({ description: { _cdata: entry.description } });
}
if (entry.content) {
isContent = true;
item.push({ "content:encoded": { _cdata: entry.content } });
}
/**
* Item Author
* http://cyber.law.harvard.edu/rss/rss.html#ltauthorgtSubelementOfLtitemgt
*/
if (Array.isArray(entry.author)) {
entry.author.some(function (author) {
if (author.email && author.name) {
item.push({ author: author.email + " (" + author.name + ")" });
return true;
} else if (author.name) {
rss[0]._attr["xmlns:dc"] = "http://purl.org/dc/elements/1.1/";
item.push({ "dc:creator": author.name });
return true;
}
return false;
});
}
var mrss = function mrss(el, target) {
var isItem = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
/* categories for the item */
if (el.categories) {
rss[0]._attr["xmlns:media"] = "http://search.yahoo.com/mrss/";
el.categories.forEach(function (i, index) {
if (!i.value) return;
target.push({
"media:category": [i.value, {
_attr: {
scheme: i.hasOwnProperty("scheme") ? i.scheme : "http://search.yahoo.com/mrss/category_schema",
label: i.hasOwnProperty("label") ? i.label : null
}
}]
});
});
}
/* community statistics and averaged input */
if (el.community) {
rss[0]._attr["xmlns:media"] = "http://search.yahoo.com/mrss/";
var communitygroup = [];
if ((0, _has2.default)(el.community, "statistics")) {
var i = el.community.statistics;
communitygroup.push({
"media:statistics": [{ _attr: (0, _pick2.default)(i, ["views", "favorites"]) }]
});
}
if ((0, _has2.default)(el.community, "starRating")) {
var _i = el.community.starRating;
communitygroup.push({
"media:starRating": [{ _attr: (0, _pick2.default)(_i, ["average", "count", "min", "max"]) }]
});
}
if (communitygroup.length > 0) target.push({ "media:community": communitygroup });
}
/* embed if the el has it */
if (el.embed) {
rss[0]._attr["xmlns:media"] = "http://search.yahoo.com/mrss/";
target.push({
"media:embed": [{
_attr: (0, _pick2.default)(el.embed, ["url", "width", "height", "type", "allowFullScreen"])
}]
});
}
if (el.keywords) {
rss[0]._attr["xmlns:media"] = "http://search.yahoo.com/mrss/";
target.push({ "media:keywords": [el.keywords.join(", ")] });
}
if (el.subTitle) {
el.subTitle.forEach(function (i, index) {
if (!(0, _has2.default)(i, "href") || !(0, _has2.default)(i, "type" || !(0, _has2.default)(i, "lang"))) return;
rss[0]._attr["xmlns:media"] = "http://search.yahoo.com/mrss/";
target.push({
"media:subTitle": [{
_attr: (0, _pick2.default)(i, ["href", "type", "lang"])
}]
});
});
}
/* player if the el has it */
if (el.player) {
rss[0]._attr["xmlns:media"] = "http://search.yahoo.com/mrss/";
target.push({
"media:player": [{
_attr: (0, _pick2.default)(el.player, ["url", "width", "height"])
}]
});
}
/**
* Feed supports *one* MRSS media:group
*/
var mediagroup = [];
/**
* rss feed only supports 1 enclosure per el, so switching to
* MRSS and its support for multiple enclosures is the next step.
*/
if (el.torrent && isItem) {
var metainfo = el.torrent;
if (!Array.isArray(metainfo)) metainfo = [metainfo];
metainfo.forEach(function (i, index) {
var i_metainfo = i;
if (!(i instanceof Object)) i_metainfo = { url: i };
if (index == 0) {
target.push({
enclosure: [{
_attr: {
type: "application/x-bittorrent",
url: i_metainfo.url
}
}]
});
if ("size_in_bytes" in i_metainfo) {
target[target.length - 1].enclosure[0]._attr["length"] = i_metainfo.size_in_bytes;
}
} else {
if (index == 1) {
rss[0]._attr["xmlns:media"] = "http://search.yahoo.com/mrss/";
var previous_metainfo = !(metainfo[0] instanceof Object) ? { url: metainfo[0] } : metainfo[0];
mediagroup.push({
"media:peerLink": [{
_attr: {
type: "application/x-bittorrent",
href: previous_metainfo.url,
isDefault: "true"
}
}]
});
}
mediagroup.push({
"media:peerLink": [{
_attr: {
type: "application/x-bittorrent",
href: i_metainfo.url
}
}]
});
}
});
}
if (el.videos && isItem) {
el.videos.forEach(function (v) {
var videoParse = function videoParse() {
var content = [{
_attr: (0, _pick2.default)(v, ["url", "fileSize", "type", "medium", "expression", "bitrate", "framerate", "samplingrate", "channels", "duration", "height", "width", "lang"])
}];
var algo = ["md5", "sha-1"];
algo.map(function (algo) {
return (0, _has2.default)(v, algo) ? content.push({
"media:hash": [{ _attr: { algo: algo } }, v[algo]]
}) : "";
});
mrss(v, content, isItem = false);
return content;
};
mediagroup.push({
"media:content": videoParse()
});
});
}
if (mediagroup.length > 1) {
/* make redundant information for MRSS clients that only look for the media:group and its contents */
// mrss(entry, mediagroup, isItem = false) // isItem MUST be false, to prevent infinite recursion
target.push({ "media:group": mediagroup });
} else if (el.image) {
target.push({
enclosure: [{
_attr: {
length: el.image.length,
type: el.image.type,
url: el.image.url
}
}]
});
}
if (el.thumbnail) {
rss[0]._attr["xmlns:media"] = "http://search.yahoo.com/mrss/";
var thumbnail = el.thumbnail;
if (!Array.isArray(thumbnail)) thumbnail = [thumbnail];
thumbnail.forEach(function (i, index) {
var i_thumbnail = i;
if (!(i instanceof Object)) i_thumbnail = { url: i };
target.push({
"media:thumbnail": [{ _attr: { url: i_thumbnail.url } }]
});["height", "width", "time"].forEach(function (optional_attr) {
if (optional_attr in i_thumbnail) target[target.length - 1]["media:thumbnail"][0]._attr[optional_attr] = i_thumbnail[optional_attr];
});
});
}
/* el properties which make sense in a setting where MRSS attributes are already present */
if (el.title && (0, _has2.default)(rss[0]._attr, "xmlns:media")) {
target.push({
"media:title": [el.title, { _attr: { type: "plain" } }]
});
}
if (el.description && (0, _has2.default)(rss[0]._attr, "xmlns:media")) {
target.push({
"media:description": [el.description, { _attr: { type: "plain" } }]
});
}
};
mrss(entry, item);
if ((0, _has2.default)(rss[0]._attr, "xmlns:media")) item.push({ "media:rating": [item.nsfw ? "adult" : "nonadult"] });
channel.push({ item: item });
});
if (isContent) {
rss[0]._attr["xmlns:content"] = "http://purl.org/rss/1.0/modules/content/";
}
if (isAtom) {
rss[0]._attr["xmlns:atom"] = "http://www.w3.org/2005/Atom";
}
/**
* Sort properties to provide reproducible results for strict implementations
*/
function sortObject(o) {
return Object.keys(o).sort().reduce(function (r, k) {
return r[k] = o[k], r;
}, {});
}
if (rss[0]._attr) rss[0]._attr = sortObject(rss[0]._attr);
return DOCTYPE + (0, _xml2.default)(root, true);
}
}, {
key: "json1",
value: function json1() {
var _this3 = this;
var options = this.options,
items = this.items,
extensions = this.extensions;
var feed = {
version: "https://jsonfeed.org/version/1",
title: options.title
};
if (options.link) {
feed.home_page_url = options.link;
}
if (options.feedLinks && options.feedLinks.json) {
feed.feed_url = options.feedLinks.json;
}
if (options.description) {
feed.description = options.description;
}
if (options.image) {
feed.icon = options.image;
}
if (options.author) {
feed.author = {};
if (options.author.name) {
feed.author.name = options.author.name;
}
if (options.author.link) {
feed.author.url = options.author.link;
}
}
extensions.forEach(function (e) {
feed[e.name] = e.objects;
});
feed.items = items.map(function (item) {
var feedItem = {
id: item.id,
// json_feed distinguishes between html and text content
// but since we only take a single type, we'll assume HTML
html_content: item.content
};
if (item.link) {
feedItem.url = item.link;
}
if (item.title) {
feedItem.title = item.title;
}
if (item.description) {
feedItem.summary = item.description;
}
if (item.torrent) {
var metainfo = item.torrent;
if (!Array.isArray(metainfo)) metainfo = [metainfo];
if (!feedItem.attachments) feedItem.attachments = [];
metainfo.forEach(function (i) {
var i_metainfo = i;
if (!(i instanceof Object)) i_metainfo = { url: i };
feedItem.attachments.push(_extends({}, i_metainfo, {
mime_type: "application/x-bittorrent"
}));
});
}
if (item.image) {
feedItem.image = item.image;
}
if (item.date) {
feedItem.date_modified = _this3.ISODateString(item.date);
}
if (item.published) {
feedItem.date_published = _this3.ISODateString(item.published);
}
if (item.author) {
var author = item.author;
if (author instanceof Array) {
// json feed only supports 1 author per post
author = author[0];
}
feedItem.author = {};
if (author.name) {
feedItem.author.name = author.name;
}
if (author.link) {
feedItem.author.url = author.link;
}
}
if (item.extensions) {
item.extensions.forEach(function (e) {
feedItem[e.name] = e.objects;
});
}
return feedItem;
});
return JSON.stringify(feed, null, 4);
}
}, {
key: "ISODateString",
value: function ISODateString(d) {
function pad(n) {
return n < 10 ? "0" + n : n;
}
return d.getUTCFullYear() + "-" + pad(d.getUTCMonth() + 1) + "-" + pad(d.getUTCDate()) + "T" + pad(d.getUTCHours()) + ":" + pad(d.getUTCMinutes()) + ":" + pad(d.getUTCSeconds()) + "Z";
}
}]);
return Feed;
}();
module.exports = Feed;
//# sourceMappingURL=feed.js.map