UNPKG

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
"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