├── .github
└── workflows
│ └── node.js.yml
├── .gitignore
├── .npmignore
├── .travis.yml
├── LICENSE
├── README.md
├── bower.json
├── dist
├── rss-parser.js
├── rss-parser.js.map
├── rss-parser.min.js
└── rss-parser.min.js.map
├── index.d.ts
├── index.js
├── lib
├── fields.js
├── parser.js
└── utils.js
├── package-lock.json
├── package.json
├── scripts
└── build.sh
├── test
├── browser.js
├── html.js
├── index.html
├── input
│ ├── atom-customfields.atom
│ ├── content-encoded.rss
│ ├── craigslist.rss
│ ├── customfields.rss
│ ├── encoding.rss
│ ├── feedburner.atom
│ ├── giantbomb-podcast.rss
│ ├── guardian.rss
│ ├── gulp-atom.atom
│ ├── heise.atom
│ ├── heraldsun.rss
│ ├── incomplete-fields.atom
│ ├── instant-article.rss
│ ├── item-itunes-episodeType.rss
│ ├── itunes-category.rss
│ ├── itunes-href.rss
│ ├── itunes-keywords-array.rss
│ ├── itunes-keywords-astext.rss
│ ├── itunes-keywords.rss
│ ├── itunes-missing-image.rss
│ ├── many-links.rss
│ ├── missing-fields.atom
│ ├── narro.rss
│ ├── pagination-links.rss
│ ├── reddit-atom.rss
│ ├── reddit-home.rss
│ ├── reddit.rss
│ ├── rss-1.rss
│ ├── unrecognized.rss
│ ├── uolNoticias.rss
│ └── xml2js-options.rss
├── output
│ ├── atom-customfields.json
│ ├── content-encoded.json
│ ├── craigslist.json
│ ├── customfields.json
│ ├── encoding.json
│ ├── feedburner.json
│ ├── giantbomb-podcast.json
│ ├── guardian.json
│ ├── gulp-atom.json
│ ├── heise.json
│ ├── heraldsun.json
│ ├── incomplete-fields.json
│ ├── instant-article.json
│ ├── item-itunes-episodeType.json
│ ├── itunes-category.json
│ ├── itunes-href.json
│ ├── itunes-keywords-array.json
│ ├── itunes-keywords-astext.json
│ ├── itunes-keywords.json
│ ├── many-links.json
│ ├── missing-fields.json
│ ├── narro.json
│ ├── pagination-links.json
│ ├── reddit-atom.json
│ ├── reddit-home.json
│ ├── reddit.json
│ ├── rss-1.json
│ ├── uolNoticias.json
│ └── xml2js-options.json
└── parser.js
└── webpack.config.js
/.github/workflows/node.js.yml:
--------------------------------------------------------------------------------
1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3 |
4 | name: tests
5 |
6 | on: [push, pull_request]
7 |
8 | jobs:
9 | build:
10 |
11 | runs-on: ubuntu-latest
12 |
13 | strategy:
14 | matrix:
15 | node-version: [14.x, 16.x, 18.x]
16 |
17 | steps:
18 | - uses: actions/checkout@v3
19 | - name: Use Node.js ${{ matrix.node-version }}
20 | uses: actions/setup-node@v3
21 | with:
22 | node-version: ${{ matrix.node-version }}
23 | - run: npm ci
24 | - run: npm test
25 | - run: npm run build
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | *.swp
3 | .idea/
4 | dist/stats.json
5 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | *.swp
3 | .idea/
4 | dist/stats.json
5 | test
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | dist: trusty
2 |
3 | language: node_js
4 | node_js:
5 | - "8"
6 |
7 | before_script:
8 | - npm install -g mocha
9 | script: npm test
10 |
11 | addons:
12 | apt:
13 | packages:
14 | - libnss3
15 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Bobby Brennan
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # rss-parser
2 |
3 | [![Version][npm-image]][npm-link]
4 | [![Build Status][build-image]][build-link]
5 | [![Downloads][downloads-image]][npm-link]
6 |
7 | [downloads-image]: https://img.shields.io/npm/dm/rss-parser.svg
8 | [npm-image]: https://img.shields.io/npm/v/rss-parser.svg
9 | [npm-link]: https://npmjs.org/package/rss-parser
10 | [build-image]: https://github.com/rbren/rss-parser/workflows/tests/badge.svg
11 | [build-link]: https://github.com/rbren/rss-parser/actions
12 |
13 | A small library for turning RSS XML feeds into JavaScript objects.
14 |
15 | ## Installation
16 | ```bash
17 | npm install --save rss-parser
18 | ```
19 |
20 | ## Usage
21 | You can parse RSS from a URL (`parser.parseURL`) or an XML string (`parser.parseString`).
22 |
23 | Both callbacks and Promises are supported.
24 |
25 | ### NodeJS
26 | Here's an example in NodeJS using Promises with async/await:
27 |
28 | ```js
29 | let Parser = require('rss-parser');
30 | let parser = new Parser();
31 |
32 | (async () => {
33 |
34 | let feed = await parser.parseURL('https://www.reddit.com/.rss');
35 | console.log(feed.title);
36 |
37 | feed.items.forEach(item => {
38 | console.log(item.title + ':' + item.link)
39 | });
40 |
41 | })();
42 | ```
43 |
44 | ### TypeScript
45 | When using TypeScript, you can set a type to control the custom fields:
46 |
47 | ```typescript
48 | import Parser from 'rss-parser';
49 |
50 | type CustomFeed = {foo: string};
51 | type CustomItem = {bar: number};
52 |
53 | const parser: Parser = new Parser({
54 | customFields: {
55 | feed: ['foo', 'baz'],
56 | // ^ will error because `baz` is not a key of CustomFeed
57 | item: ['bar']
58 | }
59 | });
60 |
61 | (async () => {
62 | const parser = new Parser();
63 | const feed = await parser.parseURL('https://www.reddit.com/.rss');
64 | console.log(feed.title); // feed will have a `foo` property, type as a string
65 |
66 | feed.items.forEach(item => {
67 | console.log(item.title + ':' + item.link) // item will have a `bar` property type as a number
68 | });
69 | })();
70 | ```
71 |
72 | ### Web
73 | > We recommend using a bundler like [webpack](https://webpack.js.org/), but we also provide
74 | > pre-built browser distributions in the `dist/` folder. If you use the pre-built distribution,
75 | > you'll need a [polyfill](https://github.com/taylorhakes/promise-polyfill) for Promise support.
76 |
77 | Here's an example in the browser using callbacks:
78 |
79 | ```html
80 |
81 |
97 | ```
98 |
99 | ### Upgrading from v2 to v3
100 | A few minor breaking changes were made in v3. Here's what you need to know:
101 |
102 | * You need to construct a `new Parser()` before calling `parseString` or `parseURL`
103 | * `parseFile` is no longer available (for better browser support)
104 | * `options` are now passed to the Parser constructor
105 | * `parsed.feed` is now just `feed` (top-level object removed)
106 | * `feed.entries` is now `feed.items` (to better match RSS XML)
107 |
108 |
109 | ## Output
110 | Check out the full output format in [test/output/reddit.json](test/output/reddit.json)
111 |
112 | ```yaml
113 | feedUrl: 'https://www.reddit.com/.rss'
114 | title: 'reddit: the front page of the internet'
115 | description: ""
116 | link: 'https://www.reddit.com/'
117 | items:
118 | - title: 'The water is too deep, so he improvises'
119 | link: 'https://www.reddit.com/r/funny/comments/3skxqc/the_water_is_too_deep_so_he_improvises/'
120 | pubDate: 'Thu, 12 Nov 2015 21:16:39 +0000'
121 | creator: "John Doe"
122 | content: 'this is a link & this is bold text '
123 | contentSnippet: 'this is a link & this is bold text'
124 | guid: 'https://www.reddit.com/r/funny/comments/3skxqc/the_water_is_too_deep_so_he_improvises/'
125 | categories:
126 | - funny
127 | isoDate: '2015-11-12T21:16:39.000Z'
128 | ```
129 |
130 | ##### Notes:
131 | * The `contentSnippet` field strips out HTML tags and unescapes HTML entities
132 | * The `dc:` prefix will be removed from all fields
133 | * Both `dc:date` and `pubDate` will be available in ISO 8601 format as `isoDate`
134 | * If `author` is specified, but not `dc:creator`, `creator` will be set to `author` ([see article](http://www.lowter.com/blogs/2008/2/9/rss-dccreator-author))
135 | * Atom's `updated` becomes `lastBuildDate` for consistency
136 |
137 | ## XML Options
138 |
139 | ### Custom Fields
140 | If your RSS feed contains fields that aren't currently returned, you can access them using the `customFields` option.
141 |
142 | ```js
143 | let parser = new Parser({
144 | customFields: {
145 | feed: ['otherTitle', 'extendedDescription'],
146 | item: ['coAuthor','subtitle'],
147 | }
148 | });
149 |
150 | parser.parseURL('https://www.reddit.com/.rss', function(err, feed) {
151 | console.log(feed.extendedDescription);
152 |
153 | feed.items.forEach(function(entry) {
154 | console.log(entry.coAuthor + ':' + entry.subtitle);
155 | })
156 | })
157 | ```
158 |
159 | To rename fields, you can pass in an array with two items, in the format `[fromField, toField]`:
160 |
161 | ```js
162 | let parser = new Parser({
163 | customFields: {
164 | item: [
165 | ['dc:coAuthor', 'coAuthor'],
166 | ]
167 | }
168 | })
169 | ```
170 |
171 | To pass additional flags, provide an object as the third array item. Currently there is one such flag:
172 |
173 | * `keepArray (false)` - set to `true` to return *all* values for fields that can have multiple entries.
174 | * `includeSnippet (false)` - set to `true` to add an additional field, `${toField}Snippet`, with HTML stripped out
175 |
176 | ```js
177 | let parser = new Parser({
178 | customFields: {
179 | item: [
180 | ['media:content', 'media:content', {keepArray: true}],
181 | ]
182 | }
183 | })
184 | ```
185 |
186 | ### Default RSS version
187 | If your RSS Feed doesn't contain a `` tag with a `version` attribute,
188 | you can pass a `defaultRSS` option for the Parser to use:
189 | ```js
190 | let parser = new Parser({
191 | defaultRSS: 2.0
192 | });
193 | ```
194 |
195 |
196 | ### xml2js passthrough
197 | `rss-parser` uses [xml2js](https://github.com/Leonidas-from-XIV/node-xml2js)
198 | to parse XML. You can pass [these options](https://github.com/Leonidas-from-XIV/node-xml2js#options)
199 | to `new xml2js.Parser()` by specifying `options.xml2js`:
200 |
201 | ```js
202 | let parser = new Parser({
203 | xml2js: {
204 | emptyTag: '--EMPTY--',
205 | }
206 | });
207 | ```
208 |
209 | ## HTTP Options
210 |
211 | ### Timeout
212 | You can set the amount of time (in milliseconds) to wait before the HTTP request times out (default 60 seconds):
213 |
214 | ```js
215 | let parser = new Parser({
216 | timeout: 1000,
217 | });
218 | ```
219 |
220 | ### Headers
221 | You can pass headers to the HTTP request:
222 | ```js
223 | let parser = new Parser({
224 | headers: {'User-Agent': 'something different'},
225 | });
226 | ```
227 |
228 | ### Redirects
229 | By default, `parseURL` will follow up to five redirects. You can change this
230 | with `options.maxRedirects`.
231 |
232 | ```js
233 | let parser = new Parser({maxRedirects: 100});
234 | ```
235 |
236 | ### Request passthrough
237 | `rss-parser` uses [http](https://nodejs.org/docs/latest/api/http.html#http_http_get_url_options_callback)/[https](https://nodejs.org/docs/latest/api/https.html#https_https_get_url_options_callback) module
238 | to do requests. You can pass [these options](https://nodejs.org/docs/latest/api/https.html#https_https_request_options_callback)
239 | to `http.get()`/`https.get()` by specifying `options.requestOptions`:
240 |
241 | e.g. to allow unauthorized certificate
242 | ```js
243 | let parser = new Parser({
244 | requestOptions: {
245 | rejectUnauthorized: false
246 | }
247 | });
248 | ```
249 |
250 | ## Contributing
251 | Contributions are welcome! If you are adding a feature or fixing a bug, please be sure to add a [test case](https://github.com/bobby-brennan/rss-parser/tree/master/test/input)
252 |
253 | ### Running Tests
254 | The tests run the RSS parser for several sample RSS feeds in `test/input` and outputs the resulting JSON into `test/output`. If there are any changes to the output files the tests will fail.
255 |
256 | To check if your changes affect the output of any test cases, run
257 |
258 | `npm test`
259 |
260 | To update the output files with your changes, run
261 |
262 | `WRITE_GOLDEN=true npm test`
263 |
264 | ### Publishing Releases
265 | ```bash
266 | npm run build
267 | git commit -a -m "Build distribution"
268 | npm version minor # or major/patch
269 | npm publish
270 | git push --follow-tags
271 | ```
272 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rss-parser",
3 | "description": "",
4 | "version": "1.1.0",
5 | "main": "dist/rss-parser.js",
6 | "authors": [
7 | "Bobby Brennan"
8 | ],
9 | "license": "MIT",
10 | "homepage": "https://github.com/bobby-brennan/rss-parser",
11 | "moduleType": [
12 | "node"
13 | ],
14 | "ignore": [
15 | "**/.*",
16 | "node_modules",
17 | "bower_components",
18 | "test",
19 | "tests"
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 | import { Options } from 'xml2js';
2 | import { RequestOptions } from 'https';
3 |
4 | declare namespace Parser {
5 | type CustomFieldItem = keyof U | (string | { keepArray: boolean })[]
6 |
7 | export interface CustomFields {
8 | readonly feed?: Array;
9 | readonly item?: CustomFieldItem[] | CustomFieldItem[][];
10 | }
11 |
12 | export interface ParserOptions {
13 | readonly xml2js?: Options;
14 | readonly requestOptions?: RequestOptions;
15 | readonly headers?: Record;
16 | readonly defaultRSS?: number;
17 | readonly maxRedirects?: number;
18 | readonly customFields?: CustomFields;
19 | readonly timeout?: number;
20 | }
21 |
22 | export interface Enclosure {
23 | url: string;
24 | length?: number;
25 | type?: string;
26 | }
27 |
28 | export interface Item {
29 | link?: string;
30 | guid?: string;
31 | title?: string;
32 | pubDate?: string;
33 | creator?: string;
34 | summary?: string;
35 | content?: string;
36 | isoDate?: string;
37 | categories?: string[];
38 | contentSnippet?: string;
39 | enclosure?: Enclosure;
40 | }
41 |
42 | export interface PaginationLinks {
43 | self?: string;
44 | first?: string;
45 | next?: string;
46 | last?: string;
47 | prev?: string;
48 | }
49 |
50 | export interface Output {
51 | image?: {
52 | link?: string;
53 | url: string;
54 | title?: string;
55 | },
56 | paginationLinks?: PaginationLinks;
57 | link?: string;
58 | title?: string;
59 | items: (U & Item)[];
60 | feedUrl?: string;
61 | description?: string;
62 | itunes?: {
63 | [key: string]: any;
64 | image?: string;
65 | owner?: {
66 | name?: string;
67 | email?: string;
68 | };
69 | author?: string;
70 | summary?: string;
71 | explicit?: string;
72 | categories?: string[];
73 | keywords?: string[];
74 | };
75 | }
76 | }
77 |
78 | /**
79 | * Class that handles all parsing or URL, or even XML, RSS feed to JSON.
80 | */
81 | declare class Parser {
82 | /**
83 | * @param options - Parser options.
84 | */
85 | constructor(options?: Parser.ParserOptions);
86 | /**
87 | * Parse XML content to JSON.
88 | *
89 | * @param xml - The xml to be parsed.
90 | * @param callback - Traditional callback.
91 | *
92 | * @returns Promise that has the same Output as the callback.
93 | */
94 | parseString(
95 | xml: string,
96 | callback?: (err: Error, feed: Parser.Output) => void
97 | ): Promise>;
98 |
99 | /**
100 | * Parse URL content to JSON.
101 | *
102 | * @param feedUrl - The url that needs to be parsed to JSON.
103 | * @param callback - Traditional callback.
104 | * @param redirectCount - Max of redirects, default is set to five.
105 | *
106 | * @example
107 | * await parseURL('https://www.reddit.com/.rss');
108 | * parseURL('https://www.reddit.com/.rss', (err, feed) => { ... });
109 | *
110 | * @returns Promise that has the same Output as the callback.
111 | */
112 | parseURL(
113 | feedUrl: string,
114 | callback?: (err: Error, feed: Parser.Output) => void,
115 | redirectCount?: number
116 | ): Promise>;
117 | }
118 |
119 | export = Parser;
120 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = require('./lib/parser');
4 |
5 |
--------------------------------------------------------------------------------
/lib/fields.js:
--------------------------------------------------------------------------------
1 | const fields = module.exports = {};
2 |
3 | fields.feed = [
4 | ['author', 'creator'],
5 | ['dc:publisher', 'publisher'],
6 | ['dc:creator', 'creator'],
7 | ['dc:source', 'source'],
8 | ['dc:title', 'title'],
9 | ['dc:type', 'type'],
10 | 'title',
11 | 'description',
12 | 'author',
13 | 'pubDate',
14 | 'webMaster',
15 | 'managingEditor',
16 | 'generator',
17 | 'link',
18 | 'language',
19 | 'copyright',
20 | 'lastBuildDate',
21 | 'docs',
22 | 'generator',
23 | 'ttl',
24 | 'rating',
25 | 'skipHours',
26 | 'skipDays',
27 | ];
28 |
29 | fields.item = [
30 | ['author', 'creator'],
31 | ['dc:creator', 'creator'],
32 | ['dc:date', 'date'],
33 | ['dc:language', 'language'],
34 | ['dc:rights', 'rights'],
35 | ['dc:source', 'source'],
36 | ['dc:title', 'title'],
37 | 'title',
38 | 'link',
39 | 'pubDate',
40 | 'author',
41 | 'summary',
42 | ['content:encoded', 'content:encoded', {includeSnippet: true}],
43 | 'enclosure',
44 | 'dc:creator',
45 | 'dc:date',
46 | 'comments',
47 | ];
48 |
49 | var mapItunesField = function(f) {
50 | return ['itunes:' + f, f];
51 | }
52 |
53 | fields.podcastFeed = ([
54 | 'author',
55 | 'subtitle',
56 | 'summary',
57 | 'explicit'
58 | ]).map(mapItunesField);
59 |
60 | fields.podcastItem = ([
61 | 'author',
62 | 'subtitle',
63 | 'summary',
64 | 'explicit',
65 | 'duration',
66 | 'image',
67 | 'episode',
68 | 'image',
69 | 'season',
70 | 'keywords',
71 | 'episodeType'
72 | ]).map(mapItunesField);
73 |
74 |
--------------------------------------------------------------------------------
/lib/parser.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const http = require('http');
3 | const https = require('https');
4 | const xml2js = require('xml2js');
5 | const url = require('url');
6 |
7 | const fields = require('./fields');
8 | const utils = require('./utils');
9 |
10 | const DEFAULT_HEADERS = {
11 | 'User-Agent': 'rss-parser',
12 | 'Accept': 'application/rss+xml',
13 | }
14 | const DEFAULT_MAX_REDIRECTS = 5;
15 | const DEFAULT_TIMEOUT = 60000;
16 |
17 | class Parser {
18 | constructor(options={}) {
19 | options.headers = options.headers || {};
20 | options.xml2js = options.xml2js || {};
21 | options.customFields = options.customFields || {};
22 | options.customFields.item = options.customFields.item || [];
23 | options.customFields.feed = options.customFields.feed || [];
24 | options.requestOptions = options.requestOptions || {};
25 | if (!options.maxRedirects) options.maxRedirects = DEFAULT_MAX_REDIRECTS;
26 | if (!options.timeout) options.timeout = DEFAULT_TIMEOUT;
27 | this.options = options;
28 | this.xmlParser = new xml2js.Parser(this.options.xml2js);
29 | this.etags = {};
30 | this.lastModified = {};
31 | }
32 |
33 | parseString(xml, callback) {
34 | let prom = new Promise((resolve, reject) => {
35 | this.xmlParser.parseString(xml, (err, result) => {
36 | if (err) return reject(err);
37 | if (!result) {
38 | return reject(new Error('Unable to parse XML.'));
39 | }
40 | let feed = null;
41 | if (result.feed) {
42 | feed = this.buildAtomFeed(result);
43 | } else if (result.rss && result.rss.$ && result.rss.$.version && result.rss.$.version.match(/^2/)) {
44 | feed = this.buildRSS2(result);
45 | } else if (result['rdf:RDF']) {
46 | feed = this.buildRSS1(result);
47 | } else if (result.rss && result.rss.$ && result.rss.$.version && result.rss.$.version.match(/0\.9/)) {
48 | feed = this.buildRSS0_9(result);
49 | } else if (result.rss && this.options.defaultRSS) {
50 | switch(this.options.defaultRSS) {
51 | case 0.9:
52 | feed = this.buildRSS0_9(result);
53 | break;
54 | case 1:
55 | feed = this.buildRSS1(result);
56 | break;
57 | case 2:
58 | feed = this.buildRSS2(result);
59 | break;
60 | default:
61 | return reject(new Error("default RSS version not recognized."))
62 | }
63 | } else {
64 | return reject(new Error("Feed not recognized as RSS 1 or 2."))
65 | }
66 | resolve(feed);
67 | });
68 | });
69 | prom = utils.maybePromisify(callback, prom);
70 | return prom;
71 | }
72 |
73 | parseURL(feedUrl, callback, redirectCount=0) {
74 | let xml = '';
75 | let get = feedUrl.indexOf('https') === 0 ? https.get : http.get;
76 | let urlParts = url.parse(feedUrl);
77 | let headers = Object.assign({}, DEFAULT_HEADERS, this.options.headers);
78 | if (this.etags[feedUrl]) {
79 | headers['If-None-Match'] = this.etags[feedUrl];
80 | }
81 | if (this.lastModified[feedUrl]) {
82 | headers['If-Modified-Since'] = this.lastModified[feedUrl];
83 | }
84 | let timeout = null;
85 | let prom = new Promise((resolve, reject) => {
86 | const requestOpts = Object.assign({headers}, urlParts, this.options.requestOptions);
87 | let req = get(requestOpts, (res) => {
88 | if (this.options.maxRedirects && res.statusCode >= 300 && res.statusCode < 400 && res.headers['location']) {
89 | if (redirectCount === this.options.maxRedirects) {
90 | return reject(new Error("Too many redirects"));
91 | } else {
92 | const newLocation = url.resolve(feedUrl, res.headers['location']);
93 | return this.parseURL(newLocation, null, redirectCount + 1).then(resolve, reject);
94 | }
95 | } else if (res.statusCode === 304) {
96 | return resolve(null);
97 | }
98 | else if (res.statusCode >= 300) {
99 | return reject(new Error("Status code " + res.statusCode));
100 | }
101 |
102 | if (res.headers['etag']) {
103 | this.etags[feedUrl] = res.headers['etag'];
104 | }
105 |
106 | if (res.headers['last-modified']) {
107 | this.lastModified[feedUrl] = res.headers['last-modified'];
108 | }
109 |
110 | let encoding = utils.getEncodingFromContentType(res.headers['content-type']);
111 | res.setEncoding(encoding);
112 | res.on('data', (chunk) => {
113 | xml += chunk;
114 | });
115 | res.on('end', () => {
116 | return this.parseString(xml).then(resolve, reject);
117 | });
118 | })
119 | req.on('error', reject);
120 | timeout = setTimeout(() => {
121 | let err = new Error("Request timed out after " + this.options.timeout + "ms");
122 | req.destroy(err);
123 | reject(err);
124 | }, this.options.timeout);
125 | }).then(data => {
126 | clearTimeout(timeout);
127 | return Promise.resolve(data);
128 | }, e => {
129 | clearTimeout(timeout);
130 | return Promise.reject(e);
131 | });
132 | prom = utils.maybePromisify(callback, prom);
133 | return prom;
134 | }
135 |
136 | buildAtomFeed(xmlObj) {
137 | let feed = {items: []};
138 | utils.copyFromXML(xmlObj.feed, feed, this.options.customFields.feed);
139 | if (xmlObj.feed.link) {
140 | feed.link = utils.getLink(xmlObj.feed.link, 'alternate', 0);
141 | feed.feedUrl = utils.getLink(xmlObj.feed.link, 'self', 1);
142 | }
143 | if (xmlObj.feed.title) {
144 | let title = xmlObj.feed.title[0] || '';
145 | if (title._) title = title._
146 | if (title) feed.title = title;
147 | }
148 | if (xmlObj.feed.updated) {
149 | feed.lastBuildDate = xmlObj.feed.updated[0];
150 | }
151 | feed.items = (xmlObj.feed.entry || []).map(entry => this.parseItemAtom(entry));
152 | return feed;
153 | }
154 |
155 | parseItemAtom(entry) {
156 | let item = {};
157 | utils.copyFromXML(entry, item, this.options.customFields.item);
158 | if (entry.title) {
159 | let title = entry.title[0] || '';
160 | if (title._) title = title._;
161 | if (title) item.title = title;
162 | }
163 | if (entry.link && entry.link.length) {
164 | item.link = utils.getLink(entry.link, 'alternate', 0);
165 | }
166 | if (entry.published && entry.published.length && entry.published[0].length) item.pubDate = new Date(entry.published[0]).toISOString();
167 | if (!item.pubDate && entry.updated && entry.updated.length && entry.updated[0].length) item.pubDate = new Date(entry.updated[0]).toISOString();
168 | if (entry.author && entry.author.length && entry.author[0].name && entry.author[0].name.length) item.author = entry.author[0].name[0];
169 | if (entry.content && entry.content.length) {
170 | item.content = utils.getContent(entry.content[0]);
171 | item.contentSnippet = utils.getSnippet(item.content)
172 | }
173 | if (entry.summary && entry.summary.length) {
174 | item.summary = utils.getContent(entry.summary[0]);
175 | }
176 | if (entry.id) {
177 | item.id = entry.id[0];
178 | }
179 | this.setISODate(item);
180 | return item;
181 | }
182 |
183 | buildRSS0_9(xmlObj) {
184 | var channel = xmlObj.rss.channel[0];
185 | var items = channel.item;
186 | return this.buildRSS(channel, items);
187 | }
188 |
189 | buildRSS1(xmlObj) {
190 | xmlObj = xmlObj['rdf:RDF'];
191 | let channel = xmlObj.channel[0];
192 | let items = xmlObj.item;
193 | return this.buildRSS(channel, items);
194 | }
195 |
196 | buildRSS2(xmlObj) {
197 | let channel = xmlObj.rss.channel[0];
198 | let items = channel.item;
199 | let feed = this.buildRSS(channel, items);
200 | if (xmlObj.rss.$ && xmlObj.rss.$['xmlns:itunes']) {
201 | this.decorateItunes(feed, channel);
202 | }
203 | return feed;
204 | }
205 |
206 | buildRSS(channel, items) {
207 | items = items || [];
208 | let feed = {items: []};
209 | let feedFields = fields.feed.concat(this.options.customFields.feed);
210 | let itemFields = fields.item.concat(this.options.customFields.item);
211 | if (channel['atom:link'] && channel['atom:link'][0] && channel['atom:link'][0].$) {
212 | feed.feedUrl = channel['atom:link'][0].$.href;
213 | }
214 | if (channel.image && channel.image[0] && channel.image[0].url) {
215 | feed.image = {};
216 | let image = channel.image[0];
217 | if (image.link) feed.image.link = image.link[0];
218 | if (image.url) feed.image.url = image.url[0];
219 | if (image.title) feed.image.title = image.title[0];
220 | if (image.width) feed.image.width = image.width[0];
221 | if (image.height) feed.image.height = image.height[0];
222 | }
223 | const paginationLinks = this.generatePaginationLinks(channel);
224 | if (Object.keys(paginationLinks).length) {
225 | feed.paginationLinks = paginationLinks;
226 | }
227 | utils.copyFromXML(channel, feed, feedFields);
228 | feed.items = items.map(xmlItem => this.parseItemRss(xmlItem, itemFields));
229 | return feed;
230 | }
231 |
232 | parseItemRss(xmlItem, itemFields) {
233 | let item = {};
234 | utils.copyFromXML(xmlItem, item, itemFields);
235 | if (xmlItem.enclosure) {
236 | item.enclosure = xmlItem.enclosure[0].$;
237 | }
238 | if (xmlItem.description) {
239 | item.content = utils.getContent(xmlItem.description[0]);
240 | item.contentSnippet = utils.getSnippet(item.content);
241 | }
242 | if (xmlItem.guid) {
243 | item.guid = xmlItem.guid[0];
244 | if (item.guid._) item.guid = item.guid._;
245 | }
246 | if (xmlItem.$ && xmlItem.$['rdf:about']) {
247 | item['rdf:about'] = xmlItem.$['rdf:about']
248 | }
249 | if (xmlItem.category) item.categories = xmlItem.category;
250 |
251 | var mediaContent = xmlItem['media:content']?.[0]?.$ ?? null;
252 | if(mediaContent) item.mediaContent = mediaContent;
253 |
254 | this.setISODate(item);
255 | return item;
256 | }
257 |
258 | /**
259 | * Add iTunes specific fields from XML to extracted JSON
260 | *
261 | * @access public
262 | * @param {object} feed extracted
263 | * @param {object} channel parsed XML
264 | */
265 | decorateItunes(feed, channel) {
266 | let items = channel.item || [];
267 | let categories = [];
268 | feed.itunes = {}
269 |
270 | if (channel['itunes:owner']) {
271 | let owner = {};
272 |
273 | if(channel['itunes:owner'][0]['itunes:name']) {
274 | owner.name = channel['itunes:owner'][0]['itunes:name'][0];
275 | }
276 | if(channel['itunes:owner'][0]['itunes:email']) {
277 | owner.email = channel['itunes:owner'][0]['itunes:email'][0];
278 | }
279 | feed.itunes.owner = owner;
280 | }
281 |
282 | if (channel['itunes:image']) {
283 | let image;
284 | let hasImageHref = (channel['itunes:image'][0] &&
285 | channel['itunes:image'][0].$ &&
286 | channel['itunes:image'][0].$.href);
287 | image = hasImageHref ? channel['itunes:image'][0].$.href : null;
288 | if (image) {
289 | feed.itunes.image = image;
290 | }
291 | }
292 |
293 | if (channel['itunes:category']) {
294 | const categoriesWithSubs = channel['itunes:category'].map((category) => {
295 | return {
296 | name: category && category.$ && category.$.text,
297 | subs: category['itunes:category'] ?
298 | category['itunes:category']
299 | .map((subcategory) => ({
300 | name: subcategory && subcategory.$ && subcategory.$.text
301 | })) : null,
302 | };
303 | });
304 |
305 | feed.itunes.categories = categoriesWithSubs.map((category) => category.name);
306 | feed.itunes.categoriesWithSubs = categoriesWithSubs;
307 | }
308 |
309 | if (channel['itunes:keywords']) {
310 | if (channel['itunes:keywords'].length > 1) {
311 | feed.itunes.keywords = channel['itunes:keywords'].map(
312 | keyword => keyword && keyword.$ && keyword.$.text
313 | );
314 | } else {
315 | let keywords = channel['itunes:keywords'][0];
316 | if (keywords && typeof keywords._ === 'string') {
317 | keywords = keywords._;
318 | }
319 |
320 | if (keywords && keywords.$ && keywords.$.text) {
321 | feed.itunes.keywords = keywords.$.text.split(',')
322 | } else if (typeof keywords === "string") {
323 | feed.itunes.keywords = keywords.split(',');
324 | }
325 | }
326 | }
327 |
328 | utils.copyFromXML(channel, feed.itunes, fields.podcastFeed);
329 | items.forEach((item, index) => {
330 | let entry = feed.items[index];
331 | entry.itunes = {};
332 | utils.copyFromXML(item, entry.itunes, fields.podcastItem);
333 | let image = item['itunes:image'];
334 | if (image && image[0] && image[0].$ && image[0].$.href) {
335 | entry.itunes.image = image[0].$.href;
336 | }
337 | });
338 | }
339 |
340 | setISODate(item) {
341 | let date = item.pubDate || item.date;
342 | if (date) {
343 | try {
344 | item.isoDate = new Date(date.trim()).toISOString();
345 | } catch (e) {
346 | // Ignore bad date format
347 | }
348 | }
349 | }
350 |
351 | /**
352 | * Generates a pagination object where the rel attribute is the key and href attribute is the value
353 | * { self: 'self-url', first: 'first-url', ... }
354 | *
355 | * @access private
356 | * @param {Object} channel parsed XML
357 | * @returns {Object}
358 | */
359 | generatePaginationLinks(channel) {
360 | if (!channel['atom:link']) {
361 | return {};
362 | }
363 | const paginationRelAttributes = ['self', 'first', 'next', 'prev', 'last'];
364 |
365 | return channel['atom:link'].reduce((paginationLinks, link) => {
366 | if (!link.$ || !paginationRelAttributes.includes(link.$.rel)) {
367 | return paginationLinks;
368 | }
369 | paginationLinks[link.$.rel] = link.$.href;
370 | return paginationLinks;
371 | }, {});
372 | }
373 | }
374 |
375 | module.exports = Parser;
376 |
--------------------------------------------------------------------------------
/lib/utils.js:
--------------------------------------------------------------------------------
1 | const utils = module.exports = {};
2 | const entities = require('entities');
3 | const xml2js = require('xml2js');
4 |
5 | utils.stripHtml = function(str) {
6 | str = str.replace(/([^\n])<\/?(h|br|p|ul|ol|li|blockquote|section|table|tr|div)(?:.|\n)*?>([^\n])/gm, '$1\n$3')
7 | str = str.replace(/<(?:.|\n)*?>/gm, '');
8 | return str;
9 | }
10 |
11 | utils.getSnippet = function(str) {
12 | return entities.decodeHTML(utils.stripHtml(str)).trim();
13 | }
14 |
15 | utils.getLink = function(links, rel, fallbackIdx) {
16 | if (!links) return;
17 | for (let i = 0; i < links.length; ++i) {
18 | if (links[i].$.rel === rel) return links[i].$.href;
19 | }
20 | if (links[fallbackIdx]) return links[fallbackIdx].$.href;
21 | }
22 |
23 | utils.getContent = function(content) {
24 | if (typeof content._ === 'string') {
25 | return content._;
26 | } else if (typeof content === 'object') {
27 | let builder = new xml2js.Builder({headless: true, explicitRoot: true, rootName: 'div', renderOpts: {pretty: false}});
28 | return builder.buildObject(content);
29 | } else {
30 | return content;
31 | }
32 | }
33 |
34 | utils.copyFromXML = function(xml, dest, fields) {
35 | fields.forEach(function(f) {
36 | let from = f;
37 | let to = f;
38 | let options = {};
39 | if (Array.isArray(f)) {
40 | from = f[0];
41 | to = f[1];
42 | if (f.length > 2) {
43 | options = f[2];
44 | }
45 | }
46 | const { keepArray, includeSnippet } = options;
47 | if (xml[from] !== undefined){
48 | dest[to] = keepArray ? xml[from] : xml[from][0];
49 | }
50 | if (dest[to] && typeof dest[to]._ === 'string') {
51 | dest[to]=dest[to]._;
52 | }
53 | if (includeSnippet && dest[to] && typeof dest[to] === 'string') {
54 | dest[to + 'Snippet'] = utils.getSnippet(dest[to]);
55 | }
56 | })
57 | }
58 |
59 | utils.maybePromisify = function(callback, promise) {
60 | if (!callback) return promise;
61 | return promise.then(
62 | data => setTimeout(() => callback(null, data)),
63 | err => setTimeout(() => callback(err))
64 | );
65 | }
66 |
67 | const DEFAULT_ENCODING = 'utf8';
68 | const ENCODING_REGEX = /(encoding|charset)\s*=\s*(\S+)/;
69 | const SUPPORTED_ENCODINGS = ['ascii', 'utf8', 'utf16le', 'ucs2', 'base64', 'latin1', 'binary', 'hex'];
70 | const ENCODING_ALIASES = {
71 | 'utf-8': 'utf8',
72 | 'iso-8859-1': 'latin1',
73 | }
74 |
75 | utils.getEncodingFromContentType = function(contentType) {
76 | contentType = contentType || '';
77 | let match = contentType.match(ENCODING_REGEX);
78 | let encoding = (match || [])[2] || '';
79 | encoding = encoding.toLowerCase();
80 | encoding = ENCODING_ALIASES[encoding] || encoding;
81 | if (!encoding || SUPPORTED_ENCODINGS.indexOf(encoding) === -1) {
82 | encoding = DEFAULT_ENCODING;
83 | }
84 | return encoding;
85 | }
86 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rss-parser",
3 | "version": "3.13.0",
4 | "main": "index.js",
5 | "types": "index.d.ts",
6 | "scripts": {
7 | "test": "mocha --reporter-option maxDiffSize=0 --exit",
8 | "build": "./scripts/build.sh"
9 | },
10 | "author": "Bobby Brennan",
11 | "license": "MIT",
12 | "devDependencies": {
13 | "@babel/core": "^7.21.4",
14 | "@babel/preset-env": "^7.21.4",
15 | "@types/xml2js": "^0.4.3",
16 | "babel-core": "^6.26.3",
17 | "babel-loader": "^8.0.4",
18 | "babel-preset-env": "^1.7.0",
19 | "chai": "^3.4.1",
20 | "express": "^4.16.3",
21 | "mocha": "^10.2.0",
22 | "puppeteer": "^5.5.0"
23 | },
24 | "dependencies": {
25 | "entities": "^2.0.3",
26 | "webpack": "^5.90.1",
27 | "webpack-cli": "^5.1.4",
28 | "xml2js": "^0.5.0"
29 | },
30 | "directories": {
31 | "test": "test"
32 | },
33 | "repository": {
34 | "type": "git",
35 | "url": "git+https://github.com/bobby-brennan/rss-parser.git"
36 | },
37 | "bugs": {
38 | "url": "https://github.com/bobby-brennan/rss-parser/issues"
39 | },
40 | "homepage": "https://github.com/bobby-brennan/rss-parser#readme",
41 | "description": "A lightweight RSS parser, for Node and the browser",
42 | "keywords": [
43 | "RSS",
44 | "RSS to JSON",
45 | "RSS reader",
46 | "RSS parser",
47 | "RSS to JS",
48 | "Feed reader"
49 | ]
50 | }
51 |
--------------------------------------------------------------------------------
/scripts/build.sh:
--------------------------------------------------------------------------------
1 | set -e
2 | echo "Building dev..."
3 | webpack-cli --mode=development --target=web
4 | echo "Building prod..."
5 | webpack-cli --mode=production --target=web --output-filename=dist/[name].min.js --profile --json > dist/stats.json
6 |
7 |
--------------------------------------------------------------------------------
/test/browser.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const express = require('express');
4 | const expect = require('chai').expect;
5 | const Browser = require('puppeteer');
6 |
7 | let browser = null;
8 | let page = null;
9 |
10 | const PORT = 3333;
11 | const PARSE_TIMEOUT = 1000;
12 |
13 | describe('Browser', function() {
14 | if (process.env.SKIP_BROWSER_TESTS) {
15 | console.log('skipping browser tests');
16 | return;
17 | }
18 |
19 | before(function(done) {
20 | this.timeout(5000);
21 | let app = express();
22 | app.use(express.static(__dirname));
23 | app.use('/dist', express.static(__dirname + '/../dist'));
24 | app.listen(PORT, err => {
25 | if (err) return done(err);
26 | Browser.launch({args: ['--no-sandbox']})
27 | .then(b => browser = b)
28 | .then(_ => browser.newPage())
29 | .then(p => {
30 | page = p;
31 | return page.goto('http://localhost:3333/index.html');
32 | })
33 | .then(_ => done())
34 | .catch(e => done(e));
35 | });
36 | });
37 |
38 | after(() => browser.close());
39 |
40 | it('should have window.RSSParser', () => {
41 | return page.evaluate(() => {
42 | return typeof window.RSSParser;
43 | }).then(type => {
44 | expect(type).to.equal('function');
45 | })
46 | });
47 |
48 | it('should parse reddit', function() {
49 | this.timeout(PARSE_TIMEOUT + 1000);
50 | return page.evaluate(() => {
51 | var parser = new RSSParser();
52 | parser.parseURL('http://localhost:3333/input/reddit.rss', function(err, data) {
53 | window.error = err;
54 | window.reddit = data;
55 | })
56 | })
57 | .then(_ => {
58 | return new Promise(resolve => setTimeout(resolve, PARSE_TIMEOUT))
59 | })
60 | .then(_ => page.evaluate(() => {
61 | return window.error;
62 | }))
63 | .then(err => {
64 | expect(err).to.equal(null);
65 | })
66 | .then(_ => page.evaluate(() => {
67 | return window.reddit.title;
68 | }))
69 | .then(title => {
70 | expect(title).to.equal('reddit: the front page of the internet');
71 | })
72 | })
73 | })
74 |
--------------------------------------------------------------------------------
/test/html.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var fs = require('fs');
4 | var HTTP = require('http');
5 |
6 | var utils = require('../lib/utils.js');
7 |
8 | var Expect = require('chai').expect;
9 |
10 | var IN_DIR = __dirname + '/input';
11 | var OUT_DIR = __dirname + '/output';
12 |
13 | describe('Utils', function() {
14 | it('should strip HTML appropriately', () => {
15 | var testCases = [{
16 | input: 'Hello world',
17 | output: 'Hello world',
18 | }, {
19 | input: 'hi my name is',
20 | output: 'hi\nmy name is',
21 | }, {
22 | input: 'hello world',
23 | output: 'hello\nworld',
24 | }, {
25 | input: 'hello world',
26 | output: 'hello\nworld',
27 | }, {
28 | input: 'himy name is ',
29 | output: 'hi\nmy name is',
30 | }, {
31 | input: 'helloworld
myname is
',
32 | output: 'hello\nworld\nmy\nname is',
33 | }, {
34 | input: 'hellomy name is ',
35 | output: 'hellomy name is',
36 | }, {
37 | input: 'hello my name is >',
38 | output: 'hello my name is >',
39 | }]
40 | testCases.forEach(tc => {
41 | Expect('|' + utils.getSnippet(tc.input) + '|').to.equal('|' + tc.output + '|', tc.input);
42 | });
43 | })
44 | });
45 |
46 |
--------------------------------------------------------------------------------
/test/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/test/input/encoding.rss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rbren/rss-parser/e9b21691d25cc125c7a4d7be9835e318aee5bc70/test/input/encoding.rss
--------------------------------------------------------------------------------
/test/input/gulp-atom.atom:
--------------------------------------------------------------------------------
1 |
2 |
3 | tag:github.com,2008:https://github.com/gulpjs/gulp/releases
4 |
5 |
6 | Release notes from gulp
7 | 2015-06-01T23:49:41+02:00
8 |
9 | tag:github.com,2008:Repository/11167738/v3.9.0
10 | 2015-06-01T23:49:41+02:00
11 |
12 | v3.9.0
13 | <p>3.9.0</p>
14 |
15 | contra
16 |
17 |
18 |
19 |
20 | tag:github.com,2008:Repository/11167738/v3.8.11
21 | 2015-02-09T21:37:28+01:00
22 |
23 | v3.8.11
24 | <p>3.8.11</p>
25 |
26 | contra
27 |
28 |
29 |
30 |
31 | tag:github.com,2008:Repository/11167738/v3.8.10
32 | 2014-11-04T01:11:30+01:00
33 |
34 | v3.8.10
35 | <p>3.8.10</p>
36 |
37 | contra
38 |
39 |
40 |
41 |
42 | tag:github.com,2008:Repository/11167738/v3.8.9
43 | 2014-10-22T08:55:20+02:00
44 |
45 | v3.8.9
46 | <p>3.8.9</p>
47 |
48 | contra
49 |
50 |
51 |
52 |
53 | tag:github.com,2008:Repository/11167738/v3.8.8
54 | 2014-09-07T22:20:11+02:00
55 |
56 | v3.8.8
57 | <p>3.8.8</p>
58 |
59 | contra
60 |
61 |
62 |
63 |
64 | tag:github.com,2008:Repository/11167738/v3.8.7
65 | 2014-08-02T06:58:19+02:00
66 |
67 | v3.8.7
68 | <p>3.8.7</p>
69 |
70 | contra
71 |
72 |
73 |
74 |
75 | tag:github.com,2008:Repository/11167738/v3.8.6
76 | 2014-07-10T00:06:50+02:00
77 |
78 | v3.8.6
79 | <p>3.8.6</p>
80 |
81 | contra
82 |
83 |
84 |
85 |
86 | tag:github.com,2008:Repository/11167738/v3.8.5
87 | 2014-06-27T08:53:54+02:00
88 |
89 | v3.8.5
90 | <p>3.8.5</p>
91 |
92 | contra
93 |
94 |
95 |
96 |
97 | tag:github.com,2008:Repository/11167738/v3.8.4
98 | 2014-06-27T08:38:43+02:00
99 |
100 | v3.8.4
101 | <p>3.8.4</p>
102 |
103 | contra
104 |
105 |
106 |
107 |
108 | tag:github.com,2008:Repository/11167738/v3.8.3
109 | 2014-06-26T23:17:51+02:00
110 |
111 | v3.8.3
112 | <p>3.8.3</p>
113 |
114 | contra
115 |
116 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/test/input/heise.atom:
--------------------------------------------------------------------------------
1 |
2 |
3 | heise developer neueste Meldungen
4 | Informationen für Entwickler
5 | 2016-02-01T17:54:50+01:00
6 | http://www.heise.de/developer/ http://www.heise.de/developer/icons/heise_developer_logo_weiss.gif
7 |
8 |
9 | Copyright (c) 2016 Heise Medien
10 |
11 | heise online
12 |
13 |
14 |
15 | Java-Anwendungsserver: Red Hat gibt WildFly 10 frei
16 | http://heise.de/-3088438
17 | 2016-02-01T17:54:50+01:00
18 | 2016-02-01T17:22:00+01:00
19 |
20 | Die nun verfügbare Version 10 des Enterprise-Java-Servers stellt die Basis für Red Hats kommerzielle JBoss Enterprise Application Platform 7 ist zugleich das dritte größere Release seit dem Namenswechsel des Open-Source-Projekts.
21 |
23 |
24 |
25 |
26 |
27 | Die nun verfügbare Version 10 des Enterprise-Java-Servers stellt die Basis für Red Hats kommerzielle JBoss Enterprise Application Platform 7 ist zugleich das dritte größere Release seit dem Namenswechsel des Open-Source-Projekts.
28 | ]]>
29 |
30 |
31 | Scrum Day 2016: Bewerbungen für Vorträge und Workshops
32 | http://heise.de/-3088627
33 | 2016-02-01T14:29:00+01:00
34 | 2016-02-01T14:29:00+01:00
35 |
36 | Über das Programm des zehnten Scrum Day stimmen die Teilnehmer selbst ab. Doch zuvor sind Scrum-Experten dran, sich bis Ende Februar mit einem Vortrag oder Workshop zu bewerben.
37 |
39 |
40 |
41 |
42 |
43 | Über das Programm des zehnten Scrum Day stimmen die Teilnehmer selbst ab. Doch zuvor sind Scrum-Experten dran, sich bis Ende Februar mit einem Vortrag oder Workshop zu bewerben.
44 | ]]>
45 |
46 |
47 | Microsoft veröffentlicht Cordova-Erweiterung für Visual Studio Code
48 | http://heise.de/-3088372
49 | 2016-02-01T12:12:11+01:00
50 | 2016-02-01T12:12:00+01:00
51 |
52 | Mit dem für unterschiedliche Plattformen verfügbaren Code-Editor lassen sich nun auch hybride Apps für iOS, Android, Blackberry und Windows Phone auf Basis von HTML, CSS und JavaScript entwickeln.
53 |
55 |
56 |
57 |
58 |
59 | Mit dem für unterschiedliche Plattformen verfügbaren Code-Editor lassen sich nun auch hybride Apps für iOS, Android, Blackberry und Windows Phone auf Basis von HTML, CSS und JavaScript entwickeln.
60 | ]]>
61 |
62 |
63 | Ungewisse Zukunft des MySQLDumper-Projekts
64 | http://heise.de/-3088410
65 | 2016-02-01T10:34:18+01:00
66 | 2016-02-01T10:34:00+01:00
67 |
68 | Daniel Schlichtholz hat sich von der Entwicklung des PHP- beziehungsweise Perl-Skripts verabschiedet, mit dem sich MySQL-Daten sichern und gegebenenfalls wiederherstellen lassen. Fällige Anpassungen an das neue PHP 7 würden ihn zu viel Zeit kosten.
69 |
71 |
72 |
73 |
74 |
75 | Daniel Schlichtholz hat sich von der Entwicklung des PHP- beziehungsweise Perl-Skripts verabschiedet, mit dem sich MySQL-Daten sichern und gegebenenfalls wiederherstellen lassen. Fällige Anpassungen an das neue PHP 7 würden ihn zu viel Zeit kosten.
76 | ]]>
77 |
78 |
79 | Änderungen bei der Authentifizierung in Microsofts v2.0 App Model
80 | http://heise.de/-3088319
81 | 2016-02-01T10:19:01+01:00
82 | 2016-02-01T10:19:00+01:00
83 |
84 | Microsoft ändert das v2.0 Auth Protocol zur Anmeldung an die Cloud-Dienste Microsoft Account und Azure Active Directory. Entwickler müssen aufgrund der Neuerungen vermutlich vorhandenen Code anpassen.
85 |
87 |
88 |
89 |
90 |
91 | Microsoft ändert das v2.0 Auth Protocol zur Anmeldung an die Cloud-Dienste Microsoft Account und Azure Active Directory. Entwickler müssen aufgrund der Neuerungen vermutlich vorhandenen Code anpassen.
92 | ]]>
93 |
94 |
95 | Der Pragmatische Architekt: Ein gutes Szenario
96 | http://heise.de/-3088146
97 | 2016-02-01T08:24:19+01:00
98 | 2016-02-01T08:24:00+01:00
99 |
100 | Wie entwirft ein Softwarearchitekt am besten ein System? Dass es dafür kein einfaches Rezept gibt, ist der Komplexität und Heterogenität von Softwareprojekten geschuldet. Die Sache ist aber nicht hoffnungslos. Szenarien bieten für diesen Zweck ein sehr gutes Instrumentarium.
101 | Wie entwirft ein Softwarearchitekt am besten ein System? Dass es dafür kein einfaches Rezept gibt, ist der Komplexität und Heterogenität von Softwareprojekten geschuldet. Die Sache ist aber nicht hoffnungslos. Szenarien bieten für diesen Zweck ein sehr gutes Instrumentarium.
103 | ]]>
104 |
105 |
106 | Developer Snapshots: Programmierer-News in ein, zwei Sätzen
107 | http://heise.de/-3087585
108 | 2016-01-29T15:37:06+01:00
109 | 2016-01-29T15:37:00+01:00
110 |
111 | heise Developer fasst jede Woche bisher vernachlässigte, aber doch wichtige Nachrichten zu Tools, Spezifikationen oder anderem zusammen – dieses Mal u.a. mit einem OCaml-Cross-Compiler für iOS, LLVM 3.8 und Cloud9-Integration in Googles Cloud-Plattform.
112 |
114 |
115 |
116 |
117 |
118 | heise Developer fasst jede Woche bisher vernachlässigte, aber doch wichtige Nachrichten zu Tools, Spezifikationen oder anderem zusammen – dieses Mal u.a. mit einem OCaml-Cross-Compiler für iOS, LLVM 3.8 und Cloud9-Integration in Googles Cloud-Plattform.
119 | ]]>
120 |
121 |
122 | Der Dotnet-Doktor: Auslesen und Sortieren von GPX-Dateien
123 | http://heise.de/-3087150
124 | 2016-01-29T13:59:32+01:00
125 | 2016-01-29T13:59:00+01:00
126 |
127 | Die Windows PowerShell biete eine schnelle Lösung für die Aufgabe, eine größere Menge von XML-Dateien zu sortieren.
128 | Die Windows PowerShell biete eine schnelle Lösung für die Aufgabe, eine größere Menge von XML-Dateien zu sortieren.
130 | ]]>
131 |
132 |
133 | SourceForge und Slashdot wechseln erneut den Besitzer
134 | http://heise.de/-3087034
135 | 2016-01-29T14:24:17+01:00
136 | 2016-01-29T13:02:00+01:00
137 |
138 | Der neue Besitzer, ein US-amerikanisches Webmedia-Unternehmen, will offenbar den ramponierten Ruf der Hosting-Plattform für Open-Source-Projekte aufpolieren.
139 |
141 |
142 |
143 |
144 |
145 | Der neue Besitzer, ein US-amerikanisches Webmedia-Unternehmen, will offenbar den ramponierten Ruf der Hosting-Plattform für Open-Source-Projekte aufpolieren.
146 | ]]>
147 |
148 |
149 | Tales from the Web side: Patterns und Best Practices in JavaScript: Typüberprüfungen continued
150 | http://heise.de/-3086830
151 | 2016-01-29T11:46:59+01:00
152 | 2016-01-29T11:23:00+01:00
153 |
154 | Der instanceof-Operator in JavaScript kann in einigen Fällen problematisch sein kann, nämlich immer dann, wenn man mit mehreren Versionen der gleichen "Klasse" arbeitet. Nun werden einige Lösungsansätze skizziert.
155 | Der instanceof-Operator in JavaScript kann in einigen Fällen problematisch sein kann, nämlich immer dann, wenn man mit mehreren Versionen der gleichen "Klasse" arbeitet. Nun werden einige Lösungsansätze skizziert.
157 | ]]>
158 |
159 |
160 | Facebook schließt Parse-Plattform
161 | http://heise.de/-3086857
162 | 2016-01-29T12:53:33+01:00
163 | 2016-01-29T10:12:00+01:00
164 |
165 | Ein Jahr bleibt Entwicklern, die den Mobile Backend as a Service nutzen, auf ein eigenes MongoDB-basiertes Angebot zu migrieren oder den nun als Open Source verfügbaren Parse-Server zu verwenden.
166 |
168 |
169 |
170 |
171 |
172 | Ein Jahr bleibt Entwicklern, die den Mobile Backend as a Service nutzen, auf ein eigenes MongoDB-basiertes Angebot zu migrieren oder den nun als Open Source verfügbaren Parse-Server zu verwenden.
173 | ]]>
174 |
175 |
176 | Continuous Lifecycle London: Programm online, Ticketverkauf gestartet
177 | http://heise.de/-3086901
178 | 2016-01-29T10:10:17+01:00
179 | 2016-01-29T10:10:00+01:00
180 |
181 | Anfang Mai feiert die englische Version der Continuous-Lifecycle-Konferenz in London Premiere. Jez Humble und Dave Farley sind nur zwei der Sprecher aus dem nun bekannt gegebenen Programm, das Continuous Delivery, DevOps und Co. ins Zentrum stellt.
182 |
184 |
185 |
186 |
187 |
188 | Anfang Mai feiert die englische Version der Continuous-Lifecycle-Konferenz in London Premiere. Jez Humble und Dave Farley sind nur zwei der Sprecher aus dem nun bekannt gegebenen Programm, das Continuous Delivery, DevOps und Co. ins Zentrum stellt.
189 | ]]>
190 |
191 |
192 | Java Runtime Zing verdoppelt die maximale Speichergröße auf 2 TB
193 | http://heise.de/-3086807
194 | 2016-01-29T09:58:31+01:00
195 | 2016-01-29T09:58:00+01:00
196 |
197 | Die Java Virtual Machine Zing ist speziell auf speicherhungrige Anwendungen ausgelegt. Mit Version 16.1 darf der dynamische Speicher auf 2 Terabyte steigen.
198 |
200 |
201 |
202 |
203 |
204 | Die Java Virtual Machine Zing ist speziell auf speicherhungrige Anwendungen ausgelegt. Mit Version 16.1 darf der dynamische Speicher auf 2 Terabyte steigen.
205 | ]]>
206 |
207 |
208 | C# 7 – Stand der Dinge und Ausblick
209 | http://heise.de/-3086504
210 | 2016-01-29T10:33:20+01:00
211 | 2016-01-29T09:00:00+01:00
212 |
213 | Ein Blick auf die möglichen Neuerungen für C# 7 zeigt, wie sich die Sprache Anregungen jenseits des Tellerands der objektorientierten Programmierung holt.
214 |
216 |
217 |
218 |
219 |
220 | Ein Blick auf die möglichen Neuerungen für C# 7 zeigt, wie sich die Sprache Anregungen jenseits des Tellerands der objektorientierten Programmierung holt.
221 | ]]>
222 |
223 |
224 | Apache Software Foundation bekommt ein neues Logo
225 | http://heise.de/-3086458
226 | 2016-01-28T17:07:04+01:00
227 | 2016-01-28T17:07:00+01:00
228 |
229 | Das neue Logos soll zugleich die bisherige Vergangenheit und den zukunftsorientierten energischen Wachstum der Open-Source-Organisation reflektieren.
230 |
232 |
233 |
234 |
235 |
236 | Das neue Logos soll zugleich die bisherige Vergangenheit und den zukunftsorientierten energischen Wachstum der Open-Source-Organisation reflektieren.
237 | ]]>
238 |
239 |
240 |
241 |
--------------------------------------------------------------------------------
/test/input/heraldsun.rss:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | RSS0.92 Example
5 | http://www.oreilly.com/example/index.html
6 | This is an example RSS0.91 feed
7 | en-gb
8 | Copyright 2002, Oreilly and Associates.
9 | editor@oreilly.com
10 | webmaster@oreilly.com
11 | 5
12 | 03 Apr 02 1500 GMT
13 | 03 Apr 02 1500 GMT
14 | -
15 |
The First Item
16 | http://www.oreilly.com/example/001.html
17 | This is the first item.
18 | Another Site
19 |
20 | Business/Industries/Publishing/Publishers/Nonfiction/
21 |
22 | -
23 |
The Second Item
24 | http://www.oreilly.com/example/002.html
25 | This is the second item.
26 | Another Site
27 |
28 | Business/Industries/Publishing/Publishers/Nonfiction/
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/test/input/incomplete-fields.atom:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/test/input/instant-article.rss:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Instant Article Test
5 | https://localhost:8000
6 | 1, 2, 1, 2… check the mic!
7 | en
8 | Fri, 13 May 2016 15:14:05 GMT
9 | 2016-05-13T15:14:05Z
10 | en
11 | -
12 |
My first Instant Article
13 | https://localhost:8000
14 | Lorem ipsum
15 | <b>Lorem</b> ipsum
16 | Wed, 04 May 2016 06:53:45 GMT
17 | https://localhost:8000
18 | tobi
19 | 2016-05-04T06:53:45Z
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/test/input/item-itunes-episodeType.rss:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Baywatch Berlin
9 | de
10 | Mon, 18 Nov 2019 19:19:26 +0000
11 | Fri, 18 Jun 2021 09:57:01 +0000
12 | Das Beautiful Mind Klaas Heufer-Umlauf probiert in diesem Podcast nach über 10 Jahren weltfremden Jet Set Spaß Kontakt zur echten Welt aufzunehmen.
13 | Wie einst in der Weihnachtsgeschichte wird er dabei von seinen Freunden Thomas Schmitt und Jakob Lundt an die Hand genommen und langsam wieder mit den Themen des wahren Lebens in Kontakt gebracht. Tauchen Sie ein und seien Sie die Kugel im Flipperautomaten „Baywatch Berlin“!
14 | https://baywatch-berlin.podigee.io/
15 | Podigee (https://podigee.com)
16 | episodic
17 |
18 | https://images.podigee-cdn.net/0x,sKl1jBkJzQhalasSZk-t081rvkZ1jaMq35213ibRTr2U=/https://cdn.podigee.com/uploads/u10314/70a0b394-56a9-47ad-883e-38493268d6bb.jpg
19 | Baywatch Berlin
20 | https://baywatch-berlin.podigee.io/
21 |
22 |
23 |
24 | Klaas Heufer-Umlauf, Thomas Schmitt und Jakob Lundt
25 | no
26 | klaas,late night berlin,klaas heufer umlauf,baywatch berlin,baywatch
27 |
28 |
29 |
30 | Das Beautiful Mind Klaas Heufer-Umlauf probiert in diesem Podcast nach über 10 Jahren weltfremden Jet Set Spaß Kontakt zur echten Welt aufzunehmen.
31 | Wie einst in der Weihnachtsgeschichte wird er dabei von seinen Freunden Thomas Schmitt und Jakob Lundt an die Hand genommen und langsam wieder mit den Themen des wahren Lebens in Kontakt gebracht. Tauchen Sie ein und seien Sie die Kugel im Flipperautomaten „Baywatch Berlin“!
32 |
33 | Klaas Heufer-Umlauf, Thomas Schmitt und Jakob Lundt
34 | alexander.krawczyk@starwatch.de
35 |
36 | -
37 |
Trailer
38 | Trailer
39 | Baywatch Berlin startet am 22. November. Mit Klaas Heufer-Umlauf und den anderen.
40 | Mon, 18 Nov 2019 19:14:32 +0000
41 | https://baywatch-berlin.podigee.io/t1-trailer
42 | cc973b648d6fb41e4e5ef7e4b4ee7604
43 |
44 |
46 |
47 |
48 | 1
49 | trailer
50 | Baywatch Berlin
51 | Baywatch Berlin startet am 22. November. Mit Klaas Heufer-Umlauf und den anderen.
52 | no
53 | klaas,baywatch,berlin,late night berlin,joko und klaas,duell um die welt
54 | Klaas Heufer-Umlauf, Thomas Schmitt und Jakob Lundt
55 |
56 | 114
57 |
58 |
59 |
--------------------------------------------------------------------------------
/test/input/itunes-category.rss:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Taverncast - Happy Hour in Your Head - Since 2005
5 | Bryce Erwin, Bill Ticknor, Michelle O'Neill, Mike Monan, Aric Watson, Jennifer Albrecht, Lauren Hoban and Derek Chew
6 | http://www.taverncast.com/
7 | Order a beer, pull up a seat and join your friends at Taverncast for our patented un-scripted roundtable discussions, covering a wide range of topics with a unique wit and inviting style - all blended together, it's Happy Hour in Your Head!
8 | Beer - Talk - Fun. It's Happy Hour in Your Head!
9 | Order a beer, pull up a seat and join your friends at Taverncast for our patented un-scripted roundtable discussions, covering a wide range of topics with a unique wit and inviting style - all blended together, it's Happy Hour in Your Head!
10 | en-us
11 | Taverncast, 2015
12 | taverncast@taverncast.com
13 | Wed, 03 Aug 2005 18:00:00 GMT
14 | me mitts
15 |
16 | Taverncast
17 | taverncast@taverncast.com
18 |
19 | no
20 |
21 | http://www.taverncast.com/itunes-logo.png
22 | Taverncast - Happy Hour in Your Head - Since 2005
23 | http://www.taverncast.com/
24 | 144
25 | 300
26 |
27 | Taverncast
28 | href=http://www.taverncast.com/itunes-logo.png">
29 |
30 |
31 |
32 |
33 | -
34 |
Taverncast 62 - Temporal Anomaly
35 | http://taverncast.com/
36 | Taverncast: www.taverncast.com
37 |
38 | http://www.taverncast.com
39 | Taverncast
40 | Taverncast
41 | Taverncast tosses around the age old topic: time travel! Where would you go? Why would you go? Also, Bryce has customer service issues, and Mike buys a dog.
42 | Taverncast tosses around the age old topic: time travel! Where would you go? Why would you go? Also, Bryce has customer service issues, and Mike buys a dog.
43 | Taverncast tosses around the age old topic: time travel! Where would you go? Why would you go? Also, Bryce has customer service issues, and Mike buys a dog.
44 |
45 | http://taverncast.com/shows/taverncast-62.mp3
46 | 07 Nov 2015 12:00:00 EST
47 | Society & Culture
48 | no
49 | 1:09:50
50 | Taverncast, beer, games, hobbies, gaming, computer, geek, party, movies, comics, pop-culture, weird, comedy, humor, funny, talk, nerd, news, politics, paranormal, conspiracy, morning show, stone brewing company, time travel, back to the future, past, future, present, timeline, temporal, travel, time, dog, doodle, poodle, apple, laptop, computer, arrogant, bastard, arrogant bastard
51 |
52 |
53 |
--------------------------------------------------------------------------------
/test/input/itunes-href.rss:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Transfermarkt
5 | http://www.transfermarkt.de/rss/news
6 |
8 | Transfermarkt
9 | http://www.transfermarkt.de
10 | http://www.transfermarkt.de/images/logo.png
11 |
12 | de-de
13 | Mon, 07 Jan 2019 18:12:17 +0100
14 | http://www.transfermarkt.de/rss/news
15 | -
16 |
17 | Manager bewertet Transfers | Eberl lobt BVB für Pulisic-Deal – Hudson-Odoi kostet „fast so viel wie mein Kader“
18 |
19 | http://www.transfermarkt.de/eberl-lobt-bvb-fur-pulisic-deal-ndash-hudson-odoi-kostet-bdquo-fast-so-viel-wie-mein-kader-ldquo-/view/news/326649?rss
20 |
21 | Mon, 07 Jan 2019 17:19:19 +0100
22 |
25 |
26 | Transfermarkt
27 |
28 | http://www.transfermarkt.de/eberl-lobt-bvb-fur-pulisic-deal-ndash-hudson-odoi-kostet-bdquo-fast-so-viel-wie-mein-kader-ldquo-/view/news/326649?rss
29 |
30 | -
31 |
32 | Im zweiten Anlauf | Magdeburg verleiht Talent Harant – Fünfter FCM-Spieler in Halberstadt
33 |
34 | http://www.transfermarkt.de/magdeburg-verleiht-talent-harant-ndash-funfter-fcm-spieler-in-halberstadt/view/news/326660?rss
35 |
36 | Mon, 07 Jan 2019 16:55:00 +0100
37 |
40 |
41 | Transfermarkt
42 |
43 | http://www.transfermarkt.de/magdeburg-verleiht-talent-harant-ndash-funfter-fcm-spieler-in-halberstadt/view/news/326660?rss
44 |
45 | -
46 |
47 | Transfer fix | Iborra zurück nach Spanien: Villarreal plant langfristig mit Leicesters Reservisten
48 |
49 | http://www.transfermarkt.de/iborra-zuruck-nach-spanien-villarreal-plant-langfristig-mit-leicesters-reservisten/view/news/326658?rss
50 |
51 | Mon, 07 Jan 2019 16:29:39 +0100
52 |
55 |
56 | Transfermarkt
57 |
58 | http://www.transfermarkt.de/iborra-zuruck-nach-spanien-villarreal-plant-langfristig-mit-leicesters-reservisten/view/news/326658?rss
59 |
60 | -
61 |
62 | Jetzt bei TM.tv | BVB-Interesse an Werner: Philipp im Gegenzug nach Leipzig?
63 |
64 | http://www.transfermarkt.de/bvb-interesse-an-werner-philipp-im-gegenzug-nach-leipzig-/view/news/326652?rss
65 |
66 | Mon, 07 Jan 2019 16:00:00 +0100
67 |
70 |
71 | Transfermarkt
72 |
73 | http://www.transfermarkt.de/bvb-interesse-an-werner-philipp-im-gegenzug-nach-leipzig-/view/news/326652?rss
74 |
75 | -
76 |
77 | Rechtsverteidiger | Dritter Wintertransfer: Däne Paulsen soll Ingolstadt aus der Misere helfen
78 |
79 | http://www.transfermarkt.de/dritter-wintertransfer-dane-paulsen-soll-ingolstadt-aus-der-misere-helfen/view/news/326654?rss
80 |
81 | Mon, 07 Jan 2019 15:46:15 +0100
82 |
85 |
86 | Transfermarkt
87 |
88 | http://www.transfermarkt.de/dritter-wintertransfer-dane-paulsen-soll-ingolstadt-aus-der-misere-helfen/view/news/326654?rss
89 |
90 | -
91 |
92 | Vertrag mit Option | Baffo darf sich bis Sommer in Duisburg beweisen – „Wir mussten reagieren“
93 |
94 | http://www.transfermarkt.de/baffo-darf-sich-bis-sommer-in-duisburg-beweisen-ndash-bdquo-wir-mussten-reagieren-ldquo-/view/news/326647?rss
95 |
96 | Mon, 07 Jan 2019 15:19:03 +0100
97 |
100 |
101 | Transfermarkt
102 |
103 | http://www.transfermarkt.de/baffo-darf-sich-bis-sommer-in-duisburg-beweisen-ndash-bdquo-wir-mussten-reagieren-ldquo-/view/news/326647?rss
104 |
105 | -
106 |
107 | Nach Haidara-Transfer | Bericht: Salzburg-Führung verhinderte Wolf-Wechsel nach Leipzig – Veto von Rose
108 |
109 | http://www.transfermarkt.de/bericht-salzburg-fuhrung-verhinderte-wolf-wechsel-nach-leipzig-ndash-veto-von-rose/view/news/326640?rss
110 |
111 | Mon, 07 Jan 2019 14:47:00 +0100
112 |
115 |
116 | Transfermarkt
117 |
118 | http://www.transfermarkt.de/bericht-salzburg-fuhrung-verhinderte-wolf-wechsel-nach-leipzig-ndash-veto-von-rose/view/news/326640?rss
119 |
120 | -
121 |
122 | Wechsel in die Regionalliga | Regensburg verabschiedet „verdienten Jahn-Profi“ Kopp nach Bayreuth
123 |
124 | http://www.transfermarkt.de/regensburg-verabschiedet-bdquo-verdienten-jahn-profi-ldquo-kopp-nach-bayreuth/view/news/326639?rss
125 |
126 | Mon, 07 Jan 2019 14:15:21 +0100
127 |
130 |
131 | Transfermarkt
132 |
133 | http://www.transfermarkt.de/regensburg-verabschiedet-bdquo-verdienten-jahn-profi-ldquo-kopp-nach-bayreuth/view/news/326639?rss
134 |
135 | -
136 |
137 | Regionalliga West | Zschiesche übernimmt Trainerposten beim Bonner SC
138 |
139 | http://www.transfermarkt.de/zschiesche-ubernimmt-trainerposten-beim-bonner-sc/view/news/326633?rss
140 |
141 | Mon, 07 Jan 2019 14:01:09 +0100
142 |
145 |
146 | Transfermarkt
147 |
148 | http://www.transfermarkt.de/zschiesche-ubernimmt-trainerposten-beim-bonner-sc/view/news/326633?rss
149 |
150 | -
151 |
152 | Chelsea plant Angebot | Cagliari erwartet Rekordablöse für Barella: Teuerster Italiener nach Jorginho & Buffon?
153 |
154 | http://www.transfermarkt.de/cagliari-erwartet-rekordablose-fur-barella-teuerster-italiener-nach-jorginho-amp-buffon-/view/news/326629?rss
155 |
156 | Mon, 07 Jan 2019 13:51:00 +0100
157 |
160 |
161 | Transfermarkt
162 |
163 | http://www.transfermarkt.de/cagliari-erwartet-rekordablose-fur-barella-teuerster-italiener-nach-jorginho-amp-buffon-/view/news/326629?rss
164 |
165 |
--------------------------------------------------------------------------------
/test/input/itunes-keywords-array.rss:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 | SWR2 Impuls - Wissen aktuell
8 | https://www.swr.de/swr2/programm/SWR2-Wissen-Impuls-Podcast,swr2-impuls-podcast-100.html
9 |
10 | Neues aus Wissenschaft, Medizin, Umwelt und Bildung.
11 | Neues aus Wissenschaft, Medizin, Umwelt und Bildung.
12 | de
13 | Copyright © Südwestrundfunk
14 |
15 | https://www.swr.de/swr2/programm/Podcastbild-SWR2-Impuls,1564741489275,swr2-impuls-podcast-104~_v-16x9@2dS_-6be50a9c75559ca1aaf1d0b25bae287afdcd877a.jpg
16 | SWR2 Impuls - Wissen aktuell
17 | https://www.swr.de/swr2/programm/SWR2-Wissen-Impuls-Podcast,swr2-impuls-podcast-100.html
18 |
19 | Wed, 9 Oct 2019 16:39:00 +0200
20 | Neues aus Wissenschaft, Medizin, Umwelt und Bildung. Dazu Musik, die sich vom Mainstream abhebt. Montags bis freitags um 16:05.
21 | SWR
22 | Neues aus Wissenschaft, Medizin, Umwelt und Bildung.
23 |
24 | Südwestrundfunk
25 | podcast@swr.de
26 |
27 | Südwestrundfunk
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | No
38 | No
39 |
40 | -
41 |
Ostafrika stellt sich auf die nächste Heuschreckenplage ein
42 | https://www.swr.de/swr2/wissen/Hungersnot-Ostafrika-stellt-sich-auf-die-naechste-Heuschreckenplage-ein,ostafrika-stellt-sich-auf-naechste-heuschreckenplage-ein-100.html
43 | Nach der jüngsten Heuschreckenplage droht Ostafrika neues Unheil. Es regnet mehr als sonst. Der feuchte Boden ist ein idealer Brutkasten für die nächste Generation Heuschrecken. Die ohnehin angespannte Ernährungslage könnte sich weiter verschlechtern.
44 | Nach der jüngsten Heuschreckenplage droht Ostafrika neues Unheil. Es regnet mehr als sonst. Der feuchte Boden ist ein idealer Brutkasten für die nächste Generation Heuschrecken. Die ohnehin angespannte Ernährungslage könnte sich weiter verschlechtern.
45 | Wissen,Heuschrecken,Heuschreckenschwärme,Heuschreckenplage,Ostafrika,Natur
46 | Tue, 10 Mar 2020 16:05:00 +0100
47 | c88fbba9-c228-4724-bf41-8c193b72491c
48 |
49 |
52 | 05:09
53 |
54 |
55 |
--------------------------------------------------------------------------------
/test/input/itunes-keywords.rss:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Taverncast - Happy Hour in Your Head - Since 2005
5 | Bryce Erwin, Bill Ticknor, Michelle O'Neill, Mike Monan, Aric Watson, Jennifer Albrecht, Lauren Hoban and Derek Chew
6 | http://www.taverncast.com/
7 | Order a beer, pull up a seat and join your friends at Taverncast for our patented un-scripted roundtable discussions, covering a wide range of topics with a unique wit and inviting style - all blended together, it's Happy Hour in Your Head!
8 | Beer - Talk - Fun. It's Happy Hour in Your Head!
9 | Order a beer, pull up a seat and join your friends at Taverncast for our patented un-scripted roundtable discussions, covering a wide range of topics with a unique wit and inviting style - all blended together, it's Happy Hour in Your Head!
10 | en-us
11 | Taverncast, 2015
12 | taverncast@taverncast.com
13 | Wed, 03 Aug 2005 18:00:00 GMT
14 | me mitts
15 |
16 | Taverncast
17 | taverncast@taverncast.com
18 |
19 | no
20 |
21 | http://www.taverncast.com/itunes-logo.png
22 | Taverncast - Happy Hour in Your Head - Since 2005
23 | http://www.taverncast.com/
24 | 144
25 | 300
26 |
27 | Taverncast
28 | href=http://www.taverncast.com/itunes-logo.png">
29 | Culture,Society
30 | -
31 |
Taverncast 62 - Temporal Anomaly
32 | http://taverncast.com/
33 | Taverncast: www.taverncast.com
34 |
35 | http://www.taverncast.com
36 | Taverncast
37 | Taverncast
38 | Taverncast tosses around the age old topic: time travel! Where would you go? Why would you go? Also, Bryce has customer service issues, and Mike buys a dog.
39 | Taverncast tosses around the age old topic: time travel! Where would you go? Why would you go? Also, Bryce has customer service issues, and Mike buys a dog.
40 | Taverncast tosses around the age old topic: time travel! Where would you go? Why would you go? Also, Bryce has customer service issues, and Mike buys a dog.
41 |
42 | http://taverncast.com/shows/taverncast-62.mp3
43 | 07 Nov 2015 12:00:00 EST
44 | Society & Culture
45 | no
46 | 1:09:50
47 | Taverncast, beer, games, hobbies, gaming, computer, geek, party, movies, comics, pop-culture, weird, comedy, humor, funny, talk, nerd, news, politics, paranormal, conspiracy, morning show, stone brewing company, time travel, back to the future, past, future, present, timeline, temporal, travel, time, dog, doodle, poodle, apple, laptop, computer, arrogant, bastard, arrogant bastard
48 |
49 |
50 |
--------------------------------------------------------------------------------
/test/input/missing-fields.atom:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | tag:github.com,2008:Repository/11167738/v3.9.0
5 |
6 |
7 |
--------------------------------------------------------------------------------
/test/input/narro.rss:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | foobar on Narro
5 | http://on.narro.co/f
6 | foobar uses Narro to create a podcast of articles transcribed to audio.
7 | en
8 | All article content copyright of respective source authors.
9 | foobar@gmail.com
10 | josh@narro.co
11 | Fri, 08 Jul 2016 13:40:00 UTC
12 | gopod - http://github.com/jbckmn/gopod
13 | 20
14 | foobar@gmail.com
15 | foobar uses Narro to create a podcast of articles transcribed to audio.
16 | foobar uses Narro to create a podcast of articles transcribed to audio.
17 | no
18 |
19 | foobar
20 | foobar@gmail.com
21 |
22 |
23 |
24 | -
25 |
https://www.narro.co/article/54e703933058540300000069
26 | Listen to your reading list anywhere. Narro will take your bookmarked articles and read them back to you as a podcast.<br/> http://www.narro.co/faq<br/> <ul class="linkList"></ul>
27 | FAQ for Narro
28 | Fri, 20 Feb 2015 09:51:15 UTC
29 | foobar@gmail.com
30 | https://www.narro.co/article/54e703933058540300000069
31 | foobar@gmail.com
32 | FAQ for Narro
33 | Listen to your reading list anywhere. Narro will take your bookmarked articles and read them back to you as a podcast. ... http://www.narro.co/faq ... <ul class="linkList"></ul>
34 | no
35 | 74
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/test/input/pagination-links.rss:
--------------------------------------------------------------------------------
1 |
2 |
3 | New Show - With Episode Block
4 |
5 |
6 |
7 |
8 |
9 | https://example.test/shows/new-show-1
10 | en-us
11 | Tue, 31 Mar 2020 05:19:30 +0000
12 | New Show
13 | Steve Thompson
14 | New Show
15 |
16 | No
17 | episodic
18 |
19 | Steve Thompson
20 | steve.t@gmail.com
21 |
22 |
23 |
24 |
25 | https://example.test/test-image.jpg
26 | New Show
27 | https://example.test/shows/new-show-1
28 | New Show
29 |
30 | Yes
31 | -
32 |
The First Episode
33 | https://example.test/episode?id=283843
34 | Thu, 21 Jan 2021 18:58:00 +1100
35 | Steve Thompson
36 | The First Episode
37 | The First Episode]]>
38 | The First Episode...
39 |
40 |
41 | 39:27
42 | a6f22abe-be5c-4a37-8f4b-8be3d69b0235
43 | No
44 |
45 |
46 |
--------------------------------------------------------------------------------
/test/input/reddit-atom.rss:
--------------------------------------------------------------------------------
1 | reddit: the front page of the internet https://www.reddit.com/https://www.redditstatic.com/reddit.com.header.png reddit: the front page of the internet https://www.reddit.com/The Difficulties in Space space https://www.reddit.com/r/space/comments/42babr/the_difficulties_in_space/https://www.reddit.com/r/space/comments/42babr/the_difficulties_in_space/ Sat, 23 Jan 2016 15:40:37 +0000 The Difficulties in Space Driving for Uber in the Virginia last night pics https://www.reddit.com/r/pics/comments/42bic6/driving_for_uber_in_the_virginia_last_night/https://www.reddit.com/r/pics/comments/42bic6/driving_for_uber_in_the_virginia_last_night/ Sat, 23 Jan 2016 16:36:05 +0000 Driving for Uber in the Virginia last night Today I found out how to remotely control my high school student's computers and how to send them messages when they're not doing the right thing funny https://www.reddit.com/r/funny/comments/42b7ov/today_i_found_out_how_to_remotely_control_my_high/https://www.reddit.com/r/funny/comments/42b7ov/today_i_found_out_how_to_remotely_control_my_high/ Sat, 23 Jan 2016 15:22:27 +0000 Today I found out how to remotely control my high school student's computers and how to send them messages when they're not doing the right thing TIL that xbox360 can be used as an projector. Atleast thats what Ikea make me believe. gaming https://www.reddit.com/r/gaming/comments/42b3j9/til_that_xbox360_can_be_used_as_an_projector/https://www.reddit.com/r/gaming/comments/42b3j9/til_that_xbox360_can_be_used_as_an_projector/ Sat, 23 Jan 2016 14:51:51 +0000 TIL that xbox360 can be used as an projector. Atleast thats what Ikea make me believe. Bill Cosby wins defamation case news https://www.reddit.com/r/news/comments/42b39d/bill_cosby_wins_defamation_case/https://www.reddit.com/r/news/comments/42b39d/bill_cosby_wins_defamation_case/ Sat, 23 Jan 2016 14:49:30 +0000 submitted byObParks tonews [link] [1804 comments] Super Crab! gifs https://www.reddit.com/r/gifs/comments/42b12w/super_crab/https://www.reddit.com/r/gifs/comments/42b12w/super_crab/ Sat, 23 Jan 2016 14:32:28 +0000 Super Crab! Japan accepts 27 refugees last year, rejects 99% worldnews https://www.reddit.com/r/worldnews/comments/42axuv/japan_accepts_27_refugees_last_year_rejects_99/https://www.reddit.com/r/worldnews/comments/42axuv/japan_accepts_27_refugees_last_year_rejects_99/ Sat, 23 Jan 2016 14:05:46 +0000 submitted byconantheking toworldnews [link] [5282 comments] Tap shower time! aww https://www.reddit.com/r/aww/comments/42aukl/tap_shower_time/https://www.reddit.com/r/aww/comments/42aukl/tap_shower_time/ Sat, 23 Jan 2016 13:33:58 +0000 Tap shower time! TIL Sleeping Beauty herself only has 18 lines of dialogue in the entire movie. todayilearned https://www.reddit.com/r/todayilearned/comments/42awq6/til_sleeping_beauty_herself_only_has_18_lines_of/https://www.reddit.com/r/todayilearned/comments/42awq6/til_sleeping_beauty_herself_only_has_18_lines_of/ Sat, 23 Jan 2016 13:55:20 +0000 TIL Sleeping Beauty herself only has 18 lines of dialogue in the entire movie. The most perfect vanilla bean cheesecake I've ever made food https://www.reddit.com/r/food/comments/42b8ok/the_most_perfect_vanilla_bean_cheesecake_ive_ever/https://www.reddit.com/r/food/comments/42b8ok/the_most_perfect_vanilla_bean_cheesecake_ive_ever/ Sat, 23 Jan 2016 15:29:25 +0000 The most perfect vanilla bean cheesecake I've ever made Tobey Maguire and Leonardo DiCaprio bowling, 1989 OldSchoolCool https://www.reddit.com/r/OldSchoolCool/comments/42avr3/tobey_maguire_and_leonardo_dicaprio_bowling_1989/https://www.reddit.com/r/OldSchoolCool/comments/42avr3/tobey_maguire_and_leonardo_dicaprio_bowling_1989/ Sat, 23 Jan 2016 13:45:28 +0000 Tobey Maguire and Leonardo DiCaprio bowling, 1989 This telephone box is now an ATM mildlyinteresting https://www.reddit.com/r/mildlyinteresting/comments/42aq29/this_telephone_box_is_now_an_atm/https://www.reddit.com/r/mildlyinteresting/comments/42aq29/this_telephone_box_is_now_an_atm/ Sat, 23 Jan 2016 12:48:32 +0000 This telephone box is now an ATM Reddit cares more about Leonardo's Oscar than Leonardo does. Showerthoughts https://www.reddit.com/r/Showerthoughts/comments/42axmv/reddit_cares_more_about_leonardos_oscar_than/https://www.reddit.com/r/Showerthoughts/comments/42axmv/reddit_cares_more_about_leonardos_oscar_than/ Sat, 23 Jan 2016 14:03:51 +0000 submitted byProfessorReds toShowerthoughts [link] [341 comments] Pearl Jam donates $300,000 to Flint water crisis UpliftingNews https://www.reddit.com/r/UpliftingNews/comments/42bgbh/pearl_jam_donates_300000_to_flint_water_crisis/https://www.reddit.com/r/UpliftingNews/comments/42bgbh/pearl_jam_donates_300000_to_flint_water_crisis/ Sat, 23 Jan 2016 16:22:41 +0000 Pearl Jam donates $300,000 to Flint water crisis Which persistent misconception/myth annoys you the most? AskReddit https://www.reddit.com/r/AskReddit/comments/42asa3/which_persistent_misconceptionmyth_annoys_you_the/https://www.reddit.com/r/AskReddit/comments/42asa3/which_persistent_misconceptionmyth_annoys_you_the/ Sat, 23 Jan 2016 13:11:40 +0000 submitted byadeebchowdhury toAskReddit [link] [10672 comments] Robot solves Rubik's Cube in 1.1 seconds videos https://www.reddit.com/r/videos/comments/42b5vv/robot_solves_rubiks_cube_in_11_seconds/https://www.reddit.com/r/videos/comments/42b5vv/robot_solves_rubiks_cube_in_11_seconds/ Sat, 23 Jan 2016 15:09:31 +0000 Robot solves Rubik's Cube in 1.1 seconds I am the guy who dragged a model into the ocean and tied her up with sharks for conservation. Photographer VonWong, AMA IAmA https://www.reddit.com/r/IAmA/comments/42bx8o/i_am_the_guy_who_dragged_a_model_into_the_ocean/https://www.reddit.com/r/IAmA/comments/42bx8o/i_am_the_guy_who_dragged_a_model_into_the_ocean/ Sat, 23 Jan 2016 18:08:59 +0000 <!-- SC_OFF --><div class="md"><p>Hey Reddit! Benjamin Von Wong here, glad to finally be back here for another ama. Last week <a href="http://www.vonwong.com/blog/sharkshepherd/">I dragged a model into the shark ridden sea</a> in hopes of drawing attention towards the need for <a href="https://www.change.org/p/support-malaysian-shark-sanctuaries">a shark sanctuary in Malaysia</a>. The response from reddit has been <a href="https://www.reddit.com/r/pics/comments/41lfna/a_friend_of_mine_just_took_this_photo_during_a/">pretty huge</a>, so I wanted to answer any questions you might have about me and or life and the universe in general. </p> <p>Proof: <a href="https://twitter.com/thevonwong/status/690957921530855424">Twitter</a>, <a href="https://www.facebook.com/thevonwong/posts/10153516513968515">Facebook</a><br/> My site: <a href="http://vonwong.com">http://vonwong.com</a></p> </div><!-- SC_ON --> submitted byvonwong toIAmA [link] [339 comments] Te Hoho Rock from New Zealand's Cathedral Cove [2048×1365] Photographed by Greg Ness EarthPorn https://www.reddit.com/r/EarthPorn/comments/42at7b/te_hoho_rock_from_new_zealands_cathedral_cove/https://www.reddit.com/r/EarthPorn/comments/42at7b/te_hoho_rock_from_new_zealands_cathedral_cove/ Sat, 23 Jan 2016 13:20:30 +0000 Te Hoho Rock from New Zealand's Cathedral Cove [2048×1365] Photographed by Greg Ness A diet rich in fiber may not only protect against diabetes and heart disease, it may reduce the risk of developing lung disease, according to new research published online, ahead of print in the Annals of the American Thoracic Society. science https://www.reddit.com/r/science/comments/42az5m/a_diet_rich_in_fiber_may_not_only_protect_against/https://www.reddit.com/r/science/comments/42az5m/a_diet_rich_in_fiber_may_not_only_protect_against/ Sat, 23 Jan 2016 14:16:52 +0000 submitted bydustofoblivion123 toscience [link] [170 comments] Portrait Girl, Stanley Barros, digital, 2015 Art https://www.reddit.com/r/Art/comments/42as8q/portrait_girl_stanley_barros_digital_2015/https://www.reddit.com/r/Art/comments/42as8q/portrait_girl_stanley_barros_digital_2015/ Sat, 23 Jan 2016 13:11:18 +0000 Portrait Girl, Stanley Barros, digital, 2015 The World's Most Beautiful Bookstores books https://www.reddit.com/r/books/comments/42b9wm/the_worlds_most_beautiful_bookstores/https://www.reddit.com/r/books/comments/42b9wm/the_worlds_most_beautiful_bookstores/ Sat, 23 Jan 2016 15:37:45 +0000 The World's Most Beautiful Bookstores TIFU by sending my friend a fucked up meme. tifu https://www.reddit.com/r/tifu/comments/42bvkl/tifu_by_sending_my_friend_a_fucked_up_meme/https://www.reddit.com/r/tifu/comments/42bvkl/tifu_by_sending_my_friend_a_fucked_up_meme/ Sat, 23 Jan 2016 17:58:36 +0000 <!-- SC_OFF --><div class="md"><p>I was laying in bed this morning browsing <a href="/r/imgoingtohellforthis">/r/imgoingtohellforthis</a> when I came across <a href="https://i.imgur.com/ZKJPgtd.jpg">this meme</a> that I thought was fucking hilarious. My friends have the same fucked up sense of humor that I do so I sent it to a couple of them for a good laugh. </p> <p>I then go on Facebook and see that one of my friends posted a status that his grandma passed away last night. The same friend I just sent a meme about his grandma&#39;s vaginal juices to. </p> <p>Fuck. </p> <p>Tl;Dr: was browsing <a href="/r/imgoingtohellforthis">/r/imgoingtohellforthis</a>, now I am actually going to hell for it.</p> </div><!-- SC_ON --> submitted byCaptainFlacid totifu [link] [89 comments] What do you call 5 black people having sex? Jokes https://www.reddit.com/r/Jokes/comments/42aj2i/what_do_you_call_5_black_people_having_sex/https://www.reddit.com/r/Jokes/comments/42aj2i/what_do_you_call_5_black_people_having_sex/ Sat, 23 Jan 2016 11:31:15 +0000 <!-- SC_OFF --><div class="md"><p>A threesome</p> </div><!-- SC_ON --> submitted byillestprodigy toJokes [link] [607 comments] Mutual Peace Offerings at UFC Weigh-Ins sports https://www.reddit.com/r/sports/comments/42ayjn/mutual_peace_offerings_at_ufc_weighins/https://www.reddit.com/r/sports/comments/42ayjn/mutual_peace_offerings_at_ufc_weighins/ Sat, 23 Jan 2016 14:11:56 +0000 Mutual Peace Offerings at UFC Weigh-Ins ELI5: Why does a computer sometimes have a hard time starting up, and what exactly does a Start Up Repair do to fix it? explainlikeimfive https://www.reddit.com/r/explainlikeimfive/comments/42bh58/eli5_why_does_a_computer_sometimes_have_a_hard/https://www.reddit.com/r/explainlikeimfive/comments/42bh58/eli5_why_does_a_computer_sometimes_have_a_hard/ Sat, 23 Jan 2016 16:28:07 +0000 submitted byVY_Cannabis_Majoris toexplainlikeimfive [link] [132 comments]
--------------------------------------------------------------------------------
/test/input/unrecognized.rss:
--------------------------------------------------------------------------------
1 | Document Moved
2 | Object Moved This document may be found here
--------------------------------------------------------------------------------
/test/input/uolNoticias.rss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rbren/rss-parser/e9b21691d25cc125c7a4d7be9835e318aee5bc70/test/input/uolNoticias.rss
--------------------------------------------------------------------------------
/test/output/encoding.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rbren/rss-parser/e9b21691d25cc125c7a4d7be9835e318aee5bc70/test/output/encoding.json
--------------------------------------------------------------------------------
/test/output/gulp-atom.json:
--------------------------------------------------------------------------------
1 | {
2 | "feed": {
3 | "items": [
4 | {
5 | "title": "v3.9.0",
6 | "link": "/gulpjs/gulp/releases/tag/v3.9.0",
7 | "pubDate": "2015-06-01T21:49:41.000Z",
8 | "author": "contra",
9 | "content": "3.9.0
",
10 | "contentSnippet": "3.9.0",
11 | "id": "tag:github.com,2008:Repository/11167738/v3.9.0",
12 | "isoDate": "2015-06-01T21:49:41.000Z"
13 | },
14 | {
15 | "title": "v3.8.11",
16 | "link": "/gulpjs/gulp/releases/tag/v3.8.11",
17 | "pubDate": "2015-02-09T20:37:28.000Z",
18 | "author": "contra",
19 | "content": "3.8.11
",
20 | "contentSnippet": "3.8.11",
21 | "id": "tag:github.com,2008:Repository/11167738/v3.8.11",
22 | "isoDate": "2015-02-09T20:37:28.000Z"
23 | },
24 | {
25 | "title": "v3.8.10",
26 | "link": "/gulpjs/gulp/releases/tag/v3.8.10",
27 | "pubDate": "2014-11-04T00:11:30.000Z",
28 | "author": "contra",
29 | "content": "3.8.10
",
30 | "contentSnippet": "3.8.10",
31 | "id": "tag:github.com,2008:Repository/11167738/v3.8.10",
32 | "isoDate": "2014-11-04T00:11:30.000Z"
33 | },
34 | {
35 | "title": "v3.8.9",
36 | "link": "/gulpjs/gulp/releases/tag/v3.8.9",
37 | "pubDate": "2014-10-22T06:55:20.000Z",
38 | "author": "contra",
39 | "content": "3.8.9
",
40 | "contentSnippet": "3.8.9",
41 | "id": "tag:github.com,2008:Repository/11167738/v3.8.9",
42 | "isoDate": "2014-10-22T06:55:20.000Z"
43 | },
44 | {
45 | "title": "v3.8.8",
46 | "link": "/gulpjs/gulp/releases/tag/v3.8.8",
47 | "pubDate": "2014-09-07T20:20:11.000Z",
48 | "author": "contra",
49 | "content": "3.8.8
",
50 | "contentSnippet": "3.8.8",
51 | "id": "tag:github.com,2008:Repository/11167738/v3.8.8",
52 | "isoDate": "2014-09-07T20:20:11.000Z"
53 | },
54 | {
55 | "title": "v3.8.7",
56 | "link": "/gulpjs/gulp/releases/tag/v3.8.7",
57 | "pubDate": "2014-08-02T04:58:19.000Z",
58 | "author": "contra",
59 | "content": "3.8.7
",
60 | "contentSnippet": "3.8.7",
61 | "id": "tag:github.com,2008:Repository/11167738/v3.8.7",
62 | "isoDate": "2014-08-02T04:58:19.000Z"
63 | },
64 | {
65 | "title": "v3.8.6",
66 | "link": "/gulpjs/gulp/releases/tag/v3.8.6",
67 | "pubDate": "2014-07-09T22:06:50.000Z",
68 | "author": "contra",
69 | "content": "3.8.6
",
70 | "contentSnippet": "3.8.6",
71 | "id": "tag:github.com,2008:Repository/11167738/v3.8.6",
72 | "isoDate": "2014-07-09T22:06:50.000Z"
73 | },
74 | {
75 | "title": "v3.8.5",
76 | "link": "/gulpjs/gulp/releases/tag/v3.8.5",
77 | "pubDate": "2014-06-27T06:53:54.000Z",
78 | "author": "contra",
79 | "content": "3.8.5
",
80 | "contentSnippet": "3.8.5",
81 | "id": "tag:github.com,2008:Repository/11167738/v3.8.5",
82 | "isoDate": "2014-06-27T06:53:54.000Z"
83 | },
84 | {
85 | "title": "v3.8.4",
86 | "link": "/gulpjs/gulp/releases/tag/v3.8.4",
87 | "pubDate": "2014-06-27T06:38:43.000Z",
88 | "author": "contra",
89 | "content": "3.8.4
",
90 | "contentSnippet": "3.8.4",
91 | "id": "tag:github.com,2008:Repository/11167738/v3.8.4",
92 | "isoDate": "2014-06-27T06:38:43.000Z"
93 | },
94 | {
95 | "title": "v3.8.3",
96 | "link": "/gulpjs/gulp/releases/tag/v3.8.3",
97 | "pubDate": "2014-06-26T21:17:51.000Z",
98 | "author": "contra",
99 | "content": "3.8.3
",
100 | "contentSnippet": "3.8.3",
101 | "id": "tag:github.com,2008:Repository/11167738/v3.8.3",
102 | "isoDate": "2014-06-26T21:17:51.000Z"
103 | }
104 | ],
105 | "link": "https://github.com/gulpjs/gulp/releases",
106 | "feedUrl": "https://github.com/gulpjs/gulp/releases.atom",
107 | "title": "Release notes from gulp",
108 | "lastBuildDate": "2015-06-01T23:49:41+02:00"
109 | }
110 | }
--------------------------------------------------------------------------------
/test/output/heise.json:
--------------------------------------------------------------------------------
1 | {
2 | "feed": {
3 | "items": [
4 | {
5 | "title": "Java-Anwendungsserver: Red Hat gibt WildFly 10 frei",
6 | "link": "http://www.heise.de/developer/meldung/Java-Anwendungsserver-Red-Hat-gibt-WildFly-10-frei-3088438.html?wt_mc=rss.developer.beitrag.atom",
7 | "pubDate": "2016-02-01T16:22:00.000Z",
8 | "content": "\n \n \n \n \n \n Die nun verfügbare Version 10 des Enterprise-Java-Servers stellt die Basis für Red Hats kommerzielle JBoss Enterprise Application Platform 7 ist zugleich das dritte größere Release seit dem Namenswechsel des Open-Source-Projekts.
\n ",
9 | "contentSnippet": "Die nun verfügbare Version 10 des Enterprise-Java-Servers stellt die Basis für Red Hats kommerzielle JBoss Enterprise Application Platform 7 ist zugleich das dritte größere Release seit dem Namenswechsel des Open-Source-Projekts.",
10 | "summary": "Die nun verfügbare Version 10 des Enterprise-Java-Servers stellt die Basis für Red Hats kommerzielle JBoss Enterprise Application Platform 7 ist zugleich das dritte größere Release seit dem Namenswechsel des Open-Source-Projekts.",
11 | "id": "http://heise.de/-3088438",
12 | "isoDate": "2016-02-01T16:22:00.000Z"
13 | },
14 | {
15 | "title": "Scrum Day 2016: Bewerbungen für Vorträge und Workshops",
16 | "link": "http://www.heise.de/developer/meldung/Scrum-Day-2016-Bewerbungen-fuer-Vortraege-und-Workshops-3088627.html?wt_mc=rss.developer.beitrag.atom",
17 | "pubDate": "2016-02-01T13:29:00.000Z",
18 | "content": "\n \n \n \n \n \n Über das Programm des zehnten Scrum Day stimmen die Teilnehmer selbst ab. Doch zuvor sind Scrum-Experten dran, sich bis Ende Februar mit einem Vortrag oder Workshop zu bewerben.
\n ",
19 | "contentSnippet": "Über das Programm des zehnten Scrum Day stimmen die Teilnehmer selbst ab. Doch zuvor sind Scrum-Experten dran, sich bis Ende Februar mit einem Vortrag oder Workshop zu bewerben.",
20 | "summary": "Über das Programm des zehnten Scrum Day stimmen die Teilnehmer selbst ab. Doch zuvor sind Scrum-Experten dran, sich bis Ende Februar mit einem Vortrag oder Workshop zu bewerben.",
21 | "id": "http://heise.de/-3088627",
22 | "isoDate": "2016-02-01T13:29:00.000Z"
23 | },
24 | {
25 | "title": "Microsoft veröffentlicht Cordova-Erweiterung für Visual Studio Code",
26 | "link": "http://www.heise.de/developer/meldung/Microsoft-veroeffentlicht-Cordova-Erweiterung-fuer-Visual-Studio-Code-3088372.html?wt_mc=rss.developer.beitrag.atom",
27 | "pubDate": "2016-02-01T11:12:00.000Z",
28 | "content": "\n \n \n \n \n \n Mit dem für unterschiedliche Plattformen verfügbaren Code-Editor lassen sich nun auch hybride Apps für iOS, Android, Blackberry und Windows Phone auf Basis von HTML, CSS und JavaScript entwickeln.
\n ",
29 | "contentSnippet": "Mit dem für unterschiedliche Plattformen verfügbaren Code-Editor lassen sich nun auch hybride Apps für iOS, Android, Blackberry und Windows Phone auf Basis von HTML, CSS und JavaScript entwickeln.",
30 | "summary": "Mit dem für unterschiedliche Plattformen verfügbaren Code-Editor lassen sich nun auch hybride Apps für iOS, Android, Blackberry und Windows Phone auf Basis von HTML, CSS und JavaScript entwickeln.",
31 | "id": "http://heise.de/-3088372",
32 | "isoDate": "2016-02-01T11:12:00.000Z"
33 | },
34 | {
35 | "title": "Ungewisse Zukunft des MySQLDumper-Projekts",
36 | "link": "http://www.heise.de/developer/meldung/Ungewisse-Zukunft-des-MySQLDumper-Projekts-3088410.html?wt_mc=rss.developer.beitrag.atom",
37 | "pubDate": "2016-02-01T09:34:00.000Z",
38 | "content": "\n \n \n \n \n \n Daniel Schlichtholz hat sich von der Entwicklung des PHP- beziehungsweise Perl-Skripts verabschiedet, mit dem sich MySQL-Daten sichern und gegebenenfalls wiederherstellen lassen. Fällige Anpassungen an das neue PHP 7 würden ihn zu viel Zeit kosten.
\n ",
39 | "contentSnippet": "Daniel Schlichtholz hat sich von der Entwicklung des PHP- beziehungsweise Perl-Skripts verabschiedet, mit dem sich MySQL-Daten sichern und gegebenenfalls wiederherstellen lassen. Fällige Anpassungen an das neue PHP 7 würden ihn zu viel Zeit kosten.",
40 | "summary": "Daniel Schlichtholz hat sich von der Entwicklung des PHP- beziehungsweise Perl-Skripts verabschiedet, mit dem sich MySQL-Daten sichern und gegebenenfalls wiederherstellen lassen. Fällige Anpassungen an das neue PHP 7 würden ihn zu viel Zeit kosten.",
41 | "id": "http://heise.de/-3088410",
42 | "isoDate": "2016-02-01T09:34:00.000Z"
43 | },
44 | {
45 | "title": "Änderungen bei der Authentifizierung in Microsofts v2.0 App Model",
46 | "link": "http://www.heise.de/developer/meldung/Aenderungen-bei-der-Authentifizierung-in-Microsofts-v2-0-App-Model-3088319.html?wt_mc=rss.developer.beitrag.atom",
47 | "pubDate": "2016-02-01T09:19:00.000Z",
48 | "content": "\n \n \n \n \n \n Microsoft ändert das v2.0 Auth Protocol zur Anmeldung an die Cloud-Dienste Microsoft Account und Azure Active Directory. Entwickler müssen aufgrund der Neuerungen vermutlich vorhandenen Code anpassen.
\n ",
49 | "contentSnippet": "Microsoft ändert das v2.0 Auth Protocol zur Anmeldung an die Cloud-Dienste Microsoft Account und Azure Active Directory. Entwickler müssen aufgrund der Neuerungen vermutlich vorhandenen Code anpassen.",
50 | "summary": "Microsoft ändert das v2.0 Auth Protocol zur Anmeldung an die Cloud-Dienste Microsoft Account und Azure Active Directory. Entwickler müssen aufgrund der Neuerungen vermutlich vorhandenen Code anpassen.",
51 | "id": "http://heise.de/-3088319",
52 | "isoDate": "2016-02-01T09:19:00.000Z"
53 | },
54 | {
55 | "title": "Der Pragmatische Architekt: Ein gutes Szenario",
56 | "link": "http://www.heise.de/developer/artikel/Ein-gutes-Szenario-3088146.html?wt_mc=rss.developer.beitrag.atom",
57 | "pubDate": "2016-02-01T07:24:00.000Z",
58 | "content": "\n Wie entwirft ein Softwarearchitekt am besten ein System? Dass es dafür kein einfaches Rezept gibt, ist der Komplexität und Heterogenität von Softwareprojekten geschuldet. Die Sache ist aber nicht hoffnungslos. Szenarien bieten für diesen Zweck ein sehr gutes Instrumentarium.
\n ",
59 | "contentSnippet": "Wie entwirft ein Softwarearchitekt am besten ein System? Dass es dafür kein einfaches Rezept gibt, ist der Komplexität und Heterogenität von Softwareprojekten geschuldet. Die Sache ist aber nicht hoffnungslos. Szenarien bieten für diesen Zweck ein sehr gutes Instrumentarium.",
60 | "summary": "Wie entwirft ein Softwarearchitekt am besten ein System? Dass es dafür kein einfaches Rezept gibt, ist der Komplexität und Heterogenität von Softwareprojekten geschuldet. Die Sache ist aber nicht hoffnungslos. Szenarien bieten für diesen Zweck ein sehr gutes Instrumentarium.",
61 | "id": "http://heise.de/-3088146",
62 | "isoDate": "2016-02-01T07:24:00.000Z"
63 | },
64 | {
65 | "title": "Developer Snapshots: Programmierer-News in ein, zwei Sätzen",
66 | "link": "http://www.heise.de/developer/meldung/Developer-Snapshots-Programmierer-News-in-ein-zwei-Saetzen-3087585.html?wt_mc=rss.developer.beitrag.atom",
67 | "pubDate": "2016-01-29T14:37:00.000Z",
68 | "content": "\n \n \n \n \n \n heise Developer fasst jede Woche bisher vernachlässigte, aber doch wichtige Nachrichten zu Tools, Spezifikationen oder anderem zusammen – dieses Mal u.a. mit einem OCaml-Cross-Compiler für iOS, LLVM 3.8 und Cloud9-Integration in Googles Cloud-Plattform.
\n ",
69 | "contentSnippet": "heise Developer fasst jede Woche bisher vernachlässigte, aber doch wichtige Nachrichten zu Tools, Spezifikationen oder anderem zusammen – dieses Mal u.a. mit einem OCaml-Cross-Compiler für iOS, LLVM 3.8 und Cloud9-Integration in Googles Cloud-Plattform.",
70 | "summary": "heise Developer fasst jede Woche bisher vernachlässigte, aber doch wichtige Nachrichten zu Tools, Spezifikationen oder anderem zusammen – dieses Mal u.a. mit einem OCaml-Cross-Compiler für iOS, LLVM 3.8 und Cloud9-Integration in Googles Cloud-Plattform.",
71 | "id": "http://heise.de/-3087585",
72 | "isoDate": "2016-01-29T14:37:00.000Z"
73 | },
74 | {
75 | "title": "Der Dotnet-Doktor: Auslesen und Sortieren von GPX-Dateien ",
76 | "link": "http://www.heise.de/developer/artikel/Auslesen-und-Sortieren-von-GPX-Dateien-3087150.html?wt_mc=rss.developer.beitrag.atom",
77 | "pubDate": "2016-01-29T12:59:00.000Z",
78 | "content": "\n Die Windows PowerShell biete eine schnelle Lösung für die Aufgabe, eine größere Menge von XML-Dateien zu sortieren.
\n ",
79 | "contentSnippet": "Die Windows PowerShell biete eine schnelle Lösung für die Aufgabe, eine größere Menge von XML-Dateien zu sortieren.",
80 | "summary": "Die Windows PowerShell biete eine schnelle Lösung für die Aufgabe, eine größere Menge von XML-Dateien zu sortieren.",
81 | "id": "http://heise.de/-3087150",
82 | "isoDate": "2016-01-29T12:59:00.000Z"
83 | },
84 | {
85 | "title": "SourceForge und Slashdot wechseln erneut den Besitzer",
86 | "link": "http://www.heise.de/developer/meldung/SourceForge-und-Slashdot-wechseln-erneut-den-Besitzer-3087034.html?wt_mc=rss.developer.beitrag.atom",
87 | "pubDate": "2016-01-29T12:02:00.000Z",
88 | "content": "\n \n \n \n \n \n Der neue Besitzer, ein US-amerikanisches Webmedia-Unternehmen, will offenbar den ramponierten Ruf der Hosting-Plattform für Open-Source-Projekte aufpolieren.
\n ",
89 | "contentSnippet": "Der neue Besitzer, ein US-amerikanisches Webmedia-Unternehmen, will offenbar den ramponierten Ruf der Hosting-Plattform für Open-Source-Projekte aufpolieren.",
90 | "summary": "Der neue Besitzer, ein US-amerikanisches Webmedia-Unternehmen, will offenbar den ramponierten Ruf der Hosting-Plattform für Open-Source-Projekte aufpolieren.",
91 | "id": "http://heise.de/-3087034",
92 | "isoDate": "2016-01-29T12:02:00.000Z"
93 | },
94 | {
95 | "title": "Tales from the Web side: Patterns und Best Practices in JavaScript: Typüberprüfungen continued",
96 | "link": "http://www.heise.de/developer/artikel/Patterns-und-Best-Practices-in-JavaScript-Typueberpruefungen-continued-3086830.html?wt_mc=rss.developer.beitrag.atom",
97 | "pubDate": "2016-01-29T10:23:00.000Z",
98 | "content": "\n Der instanceof-Operator in JavaScript kann in einigen Fällen problematisch sein kann, nämlich immer dann, wenn man mit mehreren Versionen der gleichen "Klasse" arbeitet. Nun werden einige Lösungsansätze skizziert.
\n ",
99 | "contentSnippet": "Der instanceof-Operator in JavaScript kann in einigen Fällen problematisch sein kann, nämlich immer dann, wenn man mit mehreren Versionen der gleichen \"Klasse\" arbeitet. Nun werden einige Lösungsansätze skizziert.",
100 | "summary": "Der instanceof-Operator in JavaScript kann in einigen Fällen problematisch sein kann, nämlich immer dann, wenn man mit mehreren Versionen der gleichen "Klasse" arbeitet. Nun werden einige Lösungsansätze skizziert.",
101 | "id": "http://heise.de/-3086830",
102 | "isoDate": "2016-01-29T10:23:00.000Z"
103 | },
104 | {
105 | "title": "Facebook schließt Parse-Plattform",
106 | "link": "http://www.heise.de/developer/meldung/Facebook-schliesst-Parse-Plattform-3086857.html?wt_mc=rss.developer.beitrag.atom",
107 | "pubDate": "2016-01-29T09:12:00.000Z",
108 | "content": "\n \n \n \n \n \n Ein Jahr bleibt Entwicklern, die den Mobile Backend as a Service nutzen, auf ein eigenes MongoDB-basiertes Angebot zu migrieren oder den nun als Open Source verfügbaren Parse-Server zu verwenden.
\n ",
109 | "contentSnippet": "Ein Jahr bleibt Entwicklern, die den Mobile Backend as a Service nutzen, auf ein eigenes MongoDB-basiertes Angebot zu migrieren oder den nun als Open Source verfügbaren Parse-Server zu verwenden.",
110 | "summary": "Ein Jahr bleibt Entwicklern, die den Mobile Backend as a Service nutzen, auf ein eigenes MongoDB-basiertes Angebot zu migrieren oder den nun als Open Source verfügbaren Parse-Server zu verwenden.",
111 | "id": "http://heise.de/-3086857",
112 | "isoDate": "2016-01-29T09:12:00.000Z"
113 | },
114 | {
115 | "title": "Continuous Lifecycle London: Programm online, Ticketverkauf gestartet",
116 | "link": "http://www.heise.de/developer/meldung/Continuous-Lifecycle-London-Programm-online-Ticketverkauf-gestartet-3086901.html?wt_mc=rss.developer.beitrag.atom",
117 | "pubDate": "2016-01-29T09:10:00.000Z",
118 | "content": "\n \n \n \n \n \n Anfang Mai feiert die englische Version der Continuous-Lifecycle-Konferenz in London Premiere. Jez Humble und Dave Farley sind nur zwei der Sprecher aus dem nun bekannt gegebenen Programm, das Continuous Delivery, DevOps und Co. ins Zentrum stellt.
\n ",
119 | "contentSnippet": "Anfang Mai feiert die englische Version der Continuous-Lifecycle-Konferenz in London Premiere. Jez Humble und Dave Farley sind nur zwei der Sprecher aus dem nun bekannt gegebenen Programm, das Continuous Delivery, DevOps und Co. ins Zentrum stellt.",
120 | "summary": "Anfang Mai feiert die englische Version der Continuous-Lifecycle-Konferenz in London Premiere. Jez Humble und Dave Farley sind nur zwei der Sprecher aus dem nun bekannt gegebenen Programm, das Continuous Delivery, DevOps und Co. ins Zentrum stellt.",
121 | "id": "http://heise.de/-3086901",
122 | "isoDate": "2016-01-29T09:10:00.000Z"
123 | },
124 | {
125 | "title": "Java Runtime Zing verdoppelt die maximale Speichergröße auf 2 TB",
126 | "link": "http://www.heise.de/developer/meldung/Java-Runtime-Zing-verdoppelt-die-maximale-Speichergroesse-auf-2-TB-3086807.html?wt_mc=rss.developer.beitrag.atom",
127 | "pubDate": "2016-01-29T08:58:00.000Z",
128 | "content": "\n \n \n \n \n \n Die Java Virtual Machine Zing ist speziell auf speicherhungrige Anwendungen ausgelegt. Mit Version 16.1 darf der dynamische Speicher auf 2 Terabyte steigen.
\n ",
129 | "contentSnippet": "Die Java Virtual Machine Zing ist speziell auf speicherhungrige Anwendungen ausgelegt. Mit Version 16.1 darf der dynamische Speicher auf 2 Terabyte steigen.",
130 | "summary": "Die Java Virtual Machine Zing ist speziell auf speicherhungrige Anwendungen ausgelegt. Mit Version 16.1 darf der dynamische Speicher auf 2 Terabyte steigen.",
131 | "id": "http://heise.de/-3086807",
132 | "isoDate": "2016-01-29T08:58:00.000Z"
133 | },
134 | {
135 | "title": "C# 7 – Stand der Dinge und Ausblick",
136 | "link": "http://www.heise.de/developer/artikel/C-7-Stand-der-Dinge-und-Ausblick-3086504.html?wt_mc=rss.developer.beitrag.atom",
137 | "pubDate": "2016-01-29T08:00:00.000Z",
138 | "content": "\n \n \n \n \n \n Ein Blick auf die möglichen Neuerungen für C# 7 zeigt, wie sich die Sprache Anregungen jenseits des Tellerands der objektorientierten Programmierung holt.
\n ",
139 | "contentSnippet": "Ein Blick auf die möglichen Neuerungen für C# 7 zeigt, wie sich die Sprache Anregungen jenseits des Tellerands der objektorientierten Programmierung holt.",
140 | "summary": "Ein Blick auf die möglichen Neuerungen für C# 7 zeigt, wie sich die Sprache Anregungen jenseits des Tellerands der objektorientierten Programmierung holt.",
141 | "id": "http://heise.de/-3086504",
142 | "isoDate": "2016-01-29T08:00:00.000Z"
143 | },
144 | {
145 | "title": "Apache Software Foundation bekommt ein neues Logo",
146 | "link": "http://www.heise.de/developer/meldung/Apache-Software-Foundation-bekommt-ein-neues-Logo-3086458.html?wt_mc=rss.developer.beitrag.atom",
147 | "pubDate": "2016-01-28T16:07:00.000Z",
148 | "content": "\n \n \n \n \n \n Das neue Logos soll zugleich die bisherige Vergangenheit und den zukunftsorientierten energischen Wachstum der Open-Source-Organisation reflektieren.
\n ",
149 | "contentSnippet": "Das neue Logos soll zugleich die bisherige Vergangenheit und den zukunftsorientierten energischen Wachstum der Open-Source-Organisation reflektieren.",
150 | "summary": "Das neue Logos soll zugleich die bisherige Vergangenheit und den zukunftsorientierten energischen Wachstum der Open-Source-Organisation reflektieren.",
151 | "id": "http://heise.de/-3086458",
152 | "isoDate": "2016-01-28T16:07:00.000Z"
153 | }
154 | ],
155 | "link": "http://www.heise.de/developer/",
156 | "feedUrl": "http://www.heise.de/developer/rss/news-atom.xml",
157 | "title": "heise developer neueste Meldungen",
158 | "lastBuildDate": "2016-02-01T17:54:50+01:00"
159 | }
160 | }
--------------------------------------------------------------------------------
/test/output/heraldsun.json:
--------------------------------------------------------------------------------
1 | {
2 | "feed": {
3 | "items": [
4 | {
5 | "title": "The First Item",
6 | "link": "http://www.oreilly.com/example/001.html",
7 | "enclosure": {
8 | "url": "http://www.oreilly.com/001.mp3",
9 | "length": "54321",
10 | "type": "audio/mpeg"
11 | },
12 | "content": "This is the first item.",
13 | "contentSnippet": "This is the first item.",
14 | "categories": [
15 | {
16 | "_": "Business/Industries/Publishing/Publishers/Nonfiction/",
17 | "$": {
18 | "domain": "http://www.dmoz.org"
19 | }
20 | }
21 | ]
22 | },
23 | {
24 | "title": "The Second Item",
25 | "link": "http://www.oreilly.com/example/002.html",
26 | "enclosure": {
27 | "url": "http://www.oreilly.com/002.mp3",
28 | "length": "54321",
29 | "type": "audio/mpeg"
30 | },
31 | "content": "This is the second item.",
32 | "contentSnippet": "This is the second item.",
33 | "categories": [
34 | {
35 | "_": "Business/Industries/Publishing/Publishers/Nonfiction/",
36 | "$": {
37 | "domain": "http://www.dmoz.org"
38 | }
39 | }
40 | ]
41 | }
42 | ],
43 | "title": "RSS0.92 Example",
44 | "description": "This is an example RSS0.91 feed",
45 | "pubDate": "03 Apr 02 1500 GMT",
46 | "webMaster": "webmaster@oreilly.com",
47 | "managingEditor": "editor@oreilly.com",
48 | "link": "http://www.oreilly.com/example/index.html",
49 | "language": "en-gb",
50 | "copyright": "Copyright 2002, Oreilly and Associates.",
51 | "lastBuildDate": "03 Apr 02 1500 GMT",
52 | "rating": "5"
53 | }
54 | }
--------------------------------------------------------------------------------
/test/output/incomplete-fields.json:
--------------------------------------------------------------------------------
1 | {
2 | "feed": {
3 | "items": [
4 | {}
5 | ]
6 | }
7 | }
--------------------------------------------------------------------------------
/test/output/instant-article.json:
--------------------------------------------------------------------------------
1 | {
2 | "feed": {
3 | "items": [
4 | {
5 | "creator": "tobi",
6 | "date": "2016-05-04T06:53:45Z",
7 | "title": "My first Instant Article",
8 | "link": "https://localhost:8000",
9 | "pubDate": "Wed, 04 May 2016 06:53:45 GMT",
10 | "content:encoded": "Lorem ipsum",
11 | "content:encodedSnippet": "Lorem ipsum",
12 | "dc:creator": "tobi",
13 | "dc:date": "2016-05-04T06:53:45Z",
14 | "content": "Lorem ipsum",
15 | "contentSnippet": "Lorem ipsum",
16 | "guid": "https://localhost:8000",
17 | "isoDate": "2016-05-04T06:53:45.000Z"
18 | }
19 | ],
20 | "title": "Instant Article Test",
21 | "description": "1, 2, 1, 2… check the mic!",
22 | "pubDate": "Fri, 13 May 2016 15:14:05 GMT",
23 | "link": "https://localhost:8000",
24 | "language": "en"
25 | }
26 | }
--------------------------------------------------------------------------------
/test/output/item-itunes-episodeType.json:
--------------------------------------------------------------------------------
1 | {
2 | "feed": {
3 | "items": [
4 | {
5 | "title": "Trailer",
6 | "link": "https://baywatch-berlin.podigee.io/t1-trailer",
7 | "pubDate": "Mon, 18 Nov 2019 19:14:32 +0000",
8 | "content:encoded": "\n Baywatch Berlin\nBaywatch Berlin startet am 22. November. Mit Klaas Heufer-Umlauf und den anderen.\n ",
9 | "content:encodedSnippet": "Baywatch Berlin\nBaywatch Berlin startet am 22. November. Mit Klaas Heufer-Umlauf und den anderen.",
10 | "enclosure": {
11 | "url": "https://cdn.podigee.com/media/podcast_16544_baywatch_berlin_episode_1_trailer.mp3?v=1574079359&source=feed",
12 | "type": "audio/mpeg",
13 | "length": "2338520"
14 | },
15 | "content": "Baywatch Berlin startet am 22. November. Mit Klaas Heufer-Umlauf und den anderen.",
16 | "contentSnippet": "Baywatch Berlin startet am 22. November. Mit Klaas Heufer-Umlauf und den anderen.",
17 | "guid": "cc973b648d6fb41e4e5ef7e4b4ee7604",
18 | "isoDate": "2019-11-18T19:14:32.000Z",
19 | "itunes": {
20 | "author": "Klaas Heufer-Umlauf, Thomas Schmitt und Jakob Lundt",
21 | "subtitle": "Baywatch Berlin",
22 | "summary": "Baywatch Berlin startet am 22. November. Mit Klaas Heufer-Umlauf und den anderen.",
23 | "explicit": "no",
24 | "duration": "114",
25 | "image": "https://images.podigee-cdn.net/0x,sIupQrkLl-L2GPJE7IKzGC90ZhL3wmVuFpBuBNTf2TdQ=/https://cdn.podigee.com/uploads/u10314/6fd157b2-442d-4685-b78b-3bac68f0517f.jpg",
26 | "episode": "1",
27 | "keywords": "klaas,baywatch,berlin,late night berlin,joko und klaas,duell um die welt",
28 | "episodeType": "trailer"
29 | }
30 | }
31 | ],
32 | "feedUrl": "https://pubsubhubbub.appspot.com/",
33 | "image": {
34 | "link": "https://baywatch-berlin.podigee.io/",
35 | "url": "https://images.podigee-cdn.net/0x,sKl1jBkJzQhalasSZk-t081rvkZ1jaMq35213ibRTr2U=/https://cdn.podigee.com/uploads/u10314/70a0b394-56a9-47ad-883e-38493268d6bb.jpg",
36 | "title": "Baywatch Berlin"
37 | },
38 | "paginationLinks": {
39 | "self": "https://baywatch-berlin.podigee.io/feed/mp3",
40 | "first": "https://baywatch-berlin.podigee.io/feed/mp3",
41 | "last": "https://baywatch-berlin.podigee.io/feed/mp3?page=1"
42 | },
43 | "title": "Baywatch Berlin",
44 | "description": "Das Beautiful Mind Klaas Heufer-Umlauf probiert in diesem Podcast nach über 10 Jahren weltfremden Jet Set Spaß Kontakt zur echten Welt aufzunehmen. \nWie einst in der Weihnachtsgeschichte wird er dabei von seinen Freunden Thomas Schmitt und Jakob Lundt an die Hand genommen und langsam wieder mit den Themen des wahren Lebens in Kontakt gebracht. Tauchen Sie ein und seien Sie die Kugel im Flipperautomaten „Baywatch Berlin“!",
45 | "pubDate": "Mon, 18 Nov 2019 19:19:26 +0000",
46 | "generator": "Podigee (https://podigee.com)",
47 | "link": "https://baywatch-berlin.podigee.io/",
48 | "language": "de",
49 | "lastBuildDate": "Fri, 18 Jun 2021 09:57:01 +0000",
50 | "itunes": {
51 | "owner": {
52 | "name": "Klaas Heufer-Umlauf, Thomas Schmitt und Jakob Lundt",
53 | "email": "alexander.krawczyk@starwatch.de"
54 | },
55 | "image": "https://images.podigee-cdn.net/0x,sKl1jBkJzQhalasSZk-t081rvkZ1jaMq35213ibRTr2U=/https://cdn.podigee.com/uploads/u10314/70a0b394-56a9-47ad-883e-38493268d6bb.jpg",
56 | "categories": [
57 | "Comedy",
58 | "Society & Culture",
59 | "TV & Film"
60 | ],
61 | "categoriesWithSubs": [
62 | {
63 | "name": "Comedy",
64 | "subs": null
65 | },
66 | {
67 | "name": "Society & Culture",
68 | "subs": null
69 | },
70 | {
71 | "name": "TV & Film",
72 | "subs": null
73 | }
74 | ],
75 | "keywords": [
76 | "klaas",
77 | "late night berlin",
78 | "klaas heufer umlauf",
79 | "baywatch berlin",
80 | "baywatch"
81 | ],
82 | "author": "Klaas Heufer-Umlauf, Thomas Schmitt und Jakob Lundt",
83 | "subtitle": "",
84 | "summary": "Das Beautiful Mind Klaas Heufer-Umlauf probiert in diesem Podcast nach über 10 Jahren weltfremden Jet Set Spaß Kontakt zur echten Welt aufzunehmen. \nWie einst in der Weihnachtsgeschichte wird er dabei von seinen Freunden Thomas Schmitt und Jakob Lundt an die Hand genommen und langsam wieder mit den Themen des wahren Lebens in Kontakt gebracht. Tauchen Sie ein und seien Sie die Kugel im Flipperautomaten „Baywatch Berlin“!",
85 | "explicit": "no"
86 | }
87 | }
88 | }
--------------------------------------------------------------------------------
/test/output/itunes-category.json:
--------------------------------------------------------------------------------
1 | {
2 | "feed": {
3 | "items": [
4 | {
5 | "creator": "Taverncast: www.taverncast.com",
6 | "title": "Taverncast 62 - Temporal Anomaly",
7 | "link": "http://taverncast.com/",
8 | "pubDate": "07 Nov 2015 12:00:00 EST",
9 | "author": "Taverncast",
10 | "content:encoded": "Taverncast tosses around the age old topic: time travel! Where would you go? Why would you go? Also, Bryce has customer service issues, and Mike buys a dog.",
11 | "content:encodedSnippet": "Taverncast tosses around the age old topic: time travel! Where would you go? Why would you go? Also, Bryce has customer service issues, and Mike buys a dog.",
12 | "enclosure": {
13 | "url": "http://www.podtrac.com/pts/redirect.mp3?http://www.taverncast.com/shows/taverncast-62.mp3",
14 | "length": "47160424",
15 | "type": "audio/mpeg"
16 | },
17 | "dc:creator": "Taverncast: www.taverncast.com",
18 | "comments": "http://www.taverncast.com",
19 | "content": "Taverncast tosses around the age old topic: time travel! Where would you go? Why would you go? Also, Bryce has customer service issues, and Mike buys a dog.",
20 | "contentSnippet": "Taverncast tosses around the age old topic: time travel! Where would you go? Why would you go? Also, Bryce has customer service issues, and Mike buys a dog.",
21 | "guid": "http://taverncast.com/shows/taverncast-62.mp3",
22 | "categories": [
23 | "Society & Culture"
24 | ],
25 | "isoDate": "2015-11-07T17:00:00.000Z",
26 | "itunes": {
27 | "author": "Taverncast",
28 | "subtitle": "Taverncast tosses around the age old topic: time travel! Where would you go? Why would you go? Also, Bryce has customer service issues, and Mike buys a dog.",
29 | "summary": "Taverncast tosses around the age old topic: time travel! Where would you go? Why would you go? Also, Bryce has customer service issues, and Mike buys a dog.",
30 | "explicit": "no",
31 | "duration": "1:09:50",
32 | "keywords": "Taverncast, beer, games, hobbies, gaming, computer, geek, party, movies, comics, pop-culture, weird, comedy, humor, funny, talk, nerd, news, politics, paranormal, conspiracy, morning show, stone brewing company, time travel, back to the future, past, future, present, timeline, temporal, travel, time, dog, doodle, poodle, apple, laptop, computer, arrogant, bastard, arrogant bastard"
33 | }
34 | }
35 | ],
36 | "image": {
37 | "link": "http://www.taverncast.com/",
38 | "url": "http://www.taverncast.com/itunes-logo.png",
39 | "title": "Taverncast - Happy Hour in Your Head - Since 2005",
40 | "width": "144",
41 | "height": "300"
42 | },
43 | "title": "Taverncast - Happy Hour in Your Head - Since 2005",
44 | "description": "Order a beer, pull up a seat and join your friends at Taverncast for our patented un-scripted roundtable discussions, covering a wide range of topics with a unique wit and inviting style - all blended together, it's Happy Hour in Your Head!",
45 | "pubDate": "Wed, 03 Aug 2005 18:00:00 GMT",
46 | "managingEditor": "taverncast@taverncast.com",
47 | "generator": "me mitts",
48 | "link": "http://www.taverncast.com/",
49 | "language": "en-us",
50 | "copyright": "Taverncast, 2015",
51 | "itunes": {
52 | "owner": {
53 | "name": "Taverncast",
54 | "email": "taverncast@taverncast.com"
55 | },
56 | "categories": [
57 | "Society & Culture",
58 | "Games & Hobbies"
59 | ],
60 | "categoriesWithSubs": [
61 | {
62 | "name": "Society & Culture",
63 | "subs": null
64 | },
65 | {
66 | "name": "Games & Hobbies",
67 | "subs": [
68 | {
69 | "name": "Hobbies"
70 | }
71 | ]
72 | }
73 | ],
74 | "author": "Bryce Erwin, Bill Ticknor, Michelle O'Neill, Mike Monan, Aric Watson, Jennifer Albrecht, Lauren Hoban and Derek Chew",
75 | "subtitle": "Beer - Talk - Fun. It's Happy Hour in Your Head!",
76 | "summary": "Order a beer, pull up a seat and join your friends at Taverncast for our patented un-scripted roundtable discussions, covering a wide range of topics with a unique wit and inviting style - all blended together, it's Happy Hour in Your Head!",
77 | "explicit": "no"
78 | }
79 | }
80 | }
--------------------------------------------------------------------------------
/test/output/itunes-href.json:
--------------------------------------------------------------------------------
1 | {
2 | "feed": {
3 | "items": [
4 | {
5 | "creator": "\nTransfermarkt",
6 | "title": "\nManager bewertet Transfers | Eberl lobt BVB für Pulisic-Deal – Hudson-Odoi kostet „fast so viel wie mein Kader“",
7 | "link": "\nhttp://www.transfermarkt.de/eberl-lobt-bvb-fur-pulisic-deal-ndash-hudson-odoi-kostet-bdquo-fast-so-viel-wie-mein-kader-ldquo-/view/news/326649?rss",
8 | "pubDate": "\nMon, 07 Jan 2019 17:19:19 +0100",
9 | "author": "\nTransfermarkt",
10 | "content": "\nMax Eberl (Foto) hat dem BVB zum Transfererlös von Christian Pulisic (20) gratuliert. Gleichzeitig prangerte Gladbachs Sportdirektor allerdings auch derart hohe Ablösesummen an....\n",
11 | "contentSnippet": "Max Eberl (Foto) hat dem BVB zum Transfererlös von Christian Pulisic (20) gratuliert. Gleichzeitig prangerte Gladbachs Sportdirektor allerdings auch derart hohe Ablösesummen an....",
12 | "guid": "\nhttp://www.transfermarkt.de/eberl-lobt-bvb-fur-pulisic-deal-ndash-hudson-odoi-kostet-bdquo-fast-so-viel-wie-mein-kader-ldquo-/view/news/326649?rss",
13 | "isoDate": "2019-01-07T16:19:19.000Z"
14 | },
15 | {
16 | "creator": "\nTransfermarkt",
17 | "title": "\nIm zweiten Anlauf | Magdeburg verleiht Talent Harant – Fünfter FCM-Spieler in Halberstadt",
18 | "link": "\nhttp://www.transfermarkt.de/magdeburg-verleiht-talent-harant-ndash-funfter-fcm-spieler-in-halberstadt/view/news/326660?rss",
19 | "pubDate": "\nMon, 07 Jan 2019 16:55:00 +0100",
20 | "author": "\nTransfermarkt",
21 | "content": "\nDer 1.FC Magdeburg treibt seine Umstrukturierung des Kaders im Winter-Transferfenster weiter voran. Nachdem Jan Kirchhoff und Timo Perthel den Kader von Trainer Michael Oenning verstärkten und...\n",
22 | "contentSnippet": "Der 1.FC Magdeburg treibt seine Umstrukturierung des Kaders im Winter-Transferfenster weiter voran. Nachdem Jan Kirchhoff und Timo Perthel den Kader von Trainer Michael Oenning verstärkten und...",
23 | "guid": "\nhttp://www.transfermarkt.de/magdeburg-verleiht-talent-harant-ndash-funfter-fcm-spieler-in-halberstadt/view/news/326660?rss",
24 | "isoDate": "2019-01-07T15:55:00.000Z"
25 | },
26 | {
27 | "creator": "\nTransfermarkt",
28 | "title": "\nTransfer fix | Iborra zurück nach Spanien: Villarreal plant langfristig mit Leicesters Reservisten",
29 | "link": "\nhttp://www.transfermarkt.de/iborra-zuruck-nach-spanien-villarreal-plant-langfristig-mit-leicesters-reservisten/view/news/326658?rss",
30 | "pubDate": "\nMon, 07 Jan 2019 16:29:39 +0100",
31 | "author": "\nTransfermarkt",
32 | "content": "\nDer FC Villarreal hat sich mit Leicester City geeinigt und die Dienste von Vicente Iborra (30, Foto) gesichert. Bei den „Foxes“ stand der Mittelfeldspieler seit 2017 unter Vertrag, kam...\n",
33 | "contentSnippet": "Der FC Villarreal hat sich mit Leicester City geeinigt und die Dienste von Vicente Iborra (30, Foto) gesichert. Bei den „Foxes“ stand der Mittelfeldspieler seit 2017 unter Vertrag, kam...",
34 | "guid": "\nhttp://www.transfermarkt.de/iborra-zuruck-nach-spanien-villarreal-plant-langfristig-mit-leicesters-reservisten/view/news/326658?rss",
35 | "isoDate": "2019-01-07T15:29:39.000Z"
36 | },
37 | {
38 | "creator": "\nTransfermarkt",
39 | "title": "\nJetzt bei TM.tv | BVB-Interesse an Werner: Philipp im Gegenzug nach Leipzig?",
40 | "link": "\nhttp://www.transfermarkt.de/bvb-interesse-an-werner-philipp-im-gegenzug-nach-leipzig-/view/news/326652?rss",
41 | "pubDate": "\nMon, 07 Jan 2019 16:00:00 +0100",
42 | "author": "\nTransfermarkt",
43 | "content": "\nTauschen Timo Werner und Maximilian Philipp die Klubs? Nach dem „Kicker“ berichtet nun auch die „Bild“ vom Interesse des BVB an Leipzigs Werner (Foto). Zudem wird berichtet,...\n",
44 | "contentSnippet": "Tauschen Timo Werner und Maximilian Philipp die Klubs? Nach dem „Kicker“ berichtet nun auch die „Bild“ vom Interesse des BVB an Leipzigs Werner (Foto). Zudem wird berichtet,...",
45 | "guid": "\nhttp://www.transfermarkt.de/bvb-interesse-an-werner-philipp-im-gegenzug-nach-leipzig-/view/news/326652?rss",
46 | "isoDate": "2019-01-07T15:00:00.000Z"
47 | },
48 | {
49 | "creator": "\nTransfermarkt",
50 | "title": "\nRechtsverteidiger | Dritter Wintertransfer: Däne Paulsen soll Ingolstadt aus der Misere helfen",
51 | "link": "\nhttp://www.transfermarkt.de/dritter-wintertransfer-dane-paulsen-soll-ingolstadt-aus-der-misere-helfen/view/news/326654?rss",
52 | "pubDate": "\nMon, 07 Jan 2019 15:46:15 +0100",
53 | "author": "\nTransfermarkt",
54 | "content": "\nDer FC Ingolstadt hat Björn Paulsen (Foto) als seinen dritten Neuzugang in diesem Winter präsentiert. Der strauchelnde Zweitligist stattete den dänischen Rechtsverteidiger mit einem...\n",
55 | "contentSnippet": "Der FC Ingolstadt hat Björn Paulsen (Foto) als seinen dritten Neuzugang in diesem Winter präsentiert. Der strauchelnde Zweitligist stattete den dänischen Rechtsverteidiger mit einem...",
56 | "guid": "\nhttp://www.transfermarkt.de/dritter-wintertransfer-dane-paulsen-soll-ingolstadt-aus-der-misere-helfen/view/news/326654?rss",
57 | "isoDate": "2019-01-07T14:46:15.000Z"
58 | },
59 | {
60 | "creator": "\nTransfermarkt",
61 | "title": "\nVertrag mit Option | Baffo darf sich bis Sommer in Duisburg beweisen – „Wir mussten reagieren“",
62 | "link": "\nhttp://www.transfermarkt.de/baffo-darf-sich-bis-sommer-in-duisburg-beweisen-ndash-bdquo-wir-mussten-reagieren-ldquo-/view/news/326647?rss",
63 | "pubDate": "\nMon, 07 Jan 2019 15:19:03 +0100",
64 | "author": "\nTransfermarkt",
65 | "content": "\nZweitligist MSV Duisburg hat den vereinslosen Joseph Baffo (Foto) mit einem Vertrag bis Saisonende ausgestattet. Der 26-jährige Innenverteidiger war zuletzt zur Probe im Training der...\n",
66 | "contentSnippet": "Zweitligist MSV Duisburg hat den vereinslosen Joseph Baffo (Foto) mit einem Vertrag bis Saisonende ausgestattet. Der 26-jährige Innenverteidiger war zuletzt zur Probe im Training der...",
67 | "guid": "\nhttp://www.transfermarkt.de/baffo-darf-sich-bis-sommer-in-duisburg-beweisen-ndash-bdquo-wir-mussten-reagieren-ldquo-/view/news/326647?rss",
68 | "isoDate": "2019-01-07T14:19:03.000Z"
69 | },
70 | {
71 | "creator": "\nTransfermarkt",
72 | "title": "\nNach Haidara-Transfer | Bericht: Salzburg-Führung verhinderte Wolf-Wechsel nach Leipzig – Veto von Rose",
73 | "link": "\nhttp://www.transfermarkt.de/bericht-salzburg-fuhrung-verhinderte-wolf-wechsel-nach-leipzig-ndash-veto-von-rose/view/news/326640?rss",
74 | "pubDate": "\nMon, 07 Jan 2019 14:47:00 +0100",
75 | "author": "\nTransfermarkt",
76 | "content": "\nRB Leipzig ist einem Bericht zufolge bei dem Versuch gescheitert, Hannes Wolf (Foto) vom Schwester-Klub RB Salzburg nach Sachsen zu lotsen. Das meldeten die „Salzburger Nachrichten“ mit...\n",
77 | "contentSnippet": "RB Leipzig ist einem Bericht zufolge bei dem Versuch gescheitert, Hannes Wolf (Foto) vom Schwester-Klub RB Salzburg nach Sachsen zu lotsen. Das meldeten die „Salzburger Nachrichten“ mit...",
78 | "guid": "\nhttp://www.transfermarkt.de/bericht-salzburg-fuhrung-verhinderte-wolf-wechsel-nach-leipzig-ndash-veto-von-rose/view/news/326640?rss",
79 | "isoDate": "2019-01-07T13:47:00.000Z"
80 | },
81 | {
82 | "creator": "\nTransfermarkt",
83 | "title": "\nWechsel in die Regionalliga | Regensburg verabschiedet „verdienten Jahn-Profi“ Kopp nach Bayreuth",
84 | "link": "\nhttp://www.transfermarkt.de/regensburg-verabschiedet-bdquo-verdienten-jahn-profi-ldquo-kopp-nach-bayreuth/view/news/326639?rss",
85 | "pubDate": "\nMon, 07 Jan 2019 14:15:21 +0100",
86 | "author": "\nTransfermarkt",
87 | "content": "\nDefensiv-Allrounder Sven Kopp (23, Foto) verlässt den SSV Jahn Regensburg nach viereinhalb Jahren. Der Klub verkündete am Montag, dass der „verdiente Jahn-Profi“ zur SpVgg...\n",
88 | "contentSnippet": "Defensiv-Allrounder Sven Kopp (23, Foto) verlässt den SSV Jahn Regensburg nach viereinhalb Jahren. Der Klub verkündete am Montag, dass der „verdiente Jahn-Profi“ zur SpVgg...",
89 | "guid": "\nhttp://www.transfermarkt.de/regensburg-verabschiedet-bdquo-verdienten-jahn-profi-ldquo-kopp-nach-bayreuth/view/news/326639?rss",
90 | "isoDate": "2019-01-07T13:15:21.000Z"
91 | },
92 | {
93 | "creator": "\nTransfermarkt",
94 | "title": "\nRegionalliga West | Zschiesche übernimmt Trainerposten beim Bonner SC",
95 | "link": "\nhttp://www.transfermarkt.de/zschiesche-ubernimmt-trainerposten-beim-bonner-sc/view/news/326633?rss",
96 | "pubDate": "\nMon, 07 Jan 2019 14:01:09 +0100",
97 | "author": "\nTransfermarkt",
98 | "content": "\nMarkus Zschiesche (Foto) übernimmt das Traineramt beim West-Regionalligisten Bonner SC. Der 36-Jährige stand vergangene Saison für den Berliner AK 07 an der Seitenlinie und...\n",
99 | "contentSnippet": "Markus Zschiesche (Foto) übernimmt das Traineramt beim West-Regionalligisten Bonner SC. Der 36-Jährige stand vergangene Saison für den Berliner AK 07 an der Seitenlinie und...",
100 | "guid": "\nhttp://www.transfermarkt.de/zschiesche-ubernimmt-trainerposten-beim-bonner-sc/view/news/326633?rss",
101 | "isoDate": "2019-01-07T13:01:09.000Z"
102 | },
103 | {
104 | "creator": "\nTransfermarkt",
105 | "title": "\nChelsea plant Angebot | Cagliari erwartet Rekordablöse für Barella: Teuerster Italiener nach Jorginho & Buffon?",
106 | "link": "\nhttp://www.transfermarkt.de/cagliari-erwartet-rekordablose-fur-barella-teuerster-italiener-nach-jorginho-amp-buffon-/view/news/326629?rss",
107 | "pubDate": "\nMon, 07 Jan 2019 13:51:00 +0100",
108 | "author": "\nTransfermarkt",
109 | "content": "\nAbseits der italienischen Top-Klubs hat sich in den vergangenen Monaten Nicolò Barella (Foto) zu einem der Shootingstars der italienischen Serie A entwickelt. Das 21-jährige...\n",
110 | "contentSnippet": "Abseits der italienischen Top-Klubs hat sich in den vergangenen Monaten Nicolò Barella (Foto) zu einem der Shootingstars der italienischen Serie A entwickelt. Das 21-jährige...",
111 | "guid": "\nhttp://www.transfermarkt.de/cagliari-erwartet-rekordablose-fur-barella-teuerster-italiener-nach-jorginho-amp-buffon-/view/news/326629?rss",
112 | "isoDate": "2019-01-07T12:51:00.000Z"
113 | }
114 | ],
115 | "image": {
116 | "link": "http://www.transfermarkt.de",
117 | "url": "http://www.transfermarkt.de/images/logo.png",
118 | "title": "Transfermarkt"
119 | },
120 | "title": "Transfermarkt",
121 | "description": "Die 10 letzten News\n",
122 | "pubDate": "Mon, 07 Jan 2019 18:12:17 +0100",
123 | "link": "http://www.transfermarkt.de/rss/news",
124 | "language": "de-de"
125 | }
126 | }
--------------------------------------------------------------------------------
/test/output/itunes-keywords-array.json:
--------------------------------------------------------------------------------
1 | {
2 | "feed": {
3 | "items": [
4 | {
5 | "title": "Ostafrika stellt sich auf die nächste Heuschreckenplage ein",
6 | "link": "https://www.swr.de/swr2/wissen/Hungersnot-Ostafrika-stellt-sich-auf-die-naechste-Heuschreckenplage-ein,ostafrika-stellt-sich-auf-naechste-heuschreckenplage-ein-100.html",
7 | "pubDate": "Tue, 10 Mar 2020 16:05:00 +0100",
8 | "enclosure": {
9 | "length": "4668354",
10 | "type": "audio/mpeg",
11 | "url": "https://avdlswr-a.akamaihd.net/swr/swr2/wissen/impuls/beitraege/2020/03/ostafrika-droht-die-naechste-heuschreckenplage.m.mp3"
12 | },
13 | "content": "Nach der jüngsten Heuschreckenplage droht Ostafrika neues Unheil. Es regnet mehr als sonst. Der feuchte Boden ist ein idealer Brutkasten für die nächste Generation Heuschrecken. Die ohnehin angespannte Ernährungslage könnte sich weiter verschlechtern.",
14 | "contentSnippet": "Nach der jüngsten Heuschreckenplage droht Ostafrika neues Unheil. Es regnet mehr als sonst. Der feuchte Boden ist ein idealer Brutkasten für die nächste Generation Heuschrecken. Die ohnehin angespannte Ernährungslage könnte sich weiter verschlechtern.",
15 | "guid": "c88fbba9-c228-4724-bf41-8c193b72491c",
16 | "categories": [
17 | "Wissen,Heuschrecken,Heuschreckenschwärme,Heuschreckenplage,Ostafrika,Natur"
18 | ],
19 | "isoDate": "2020-03-10T15:05:00.000Z",
20 | "itunes": {
21 | "duration": "05:09",
22 | "image": "https://www.swr.de/swr2/wissen/Heuschreckeninvasion-Hochland-von-Madagaskar-Afrika,1583836906080,swr2-impuls-heuschrecken-plage-afrika-100~_v-1x1@2dXL_-1f32c27c4978132dd0854e53b5ed30e10facc189.jpg"
23 | }
24 | }
25 | ],
26 | "feedUrl": "https://www.swr.de/~podcast/swr2/programm/SWR2-Wissen-Impuls-Podcast,swr2-impuls-podcast-100.xml",
27 | "image": {
28 | "link": "https://www.swr.de/swr2/programm/SWR2-Wissen-Impuls-Podcast,swr2-impuls-podcast-100.html",
29 | "url": "https://www.swr.de/swr2/programm/Podcastbild-SWR2-Impuls,1564741489275,swr2-impuls-podcast-104~_v-16x9@2dS_-6be50a9c75559ca1aaf1d0b25bae287afdcd877a.jpg",
30 | "title": "SWR2 Impuls - Wissen aktuell"
31 | },
32 | "paginationLinks": {
33 | "self": "https://www.swr.de/~podcast/swr2/programm/SWR2-Wissen-Impuls-Podcast,swr2-impuls-podcast-100.xml"
34 | },
35 | "title": "SWR2 Impuls - Wissen aktuell",
36 | "description": "Neues aus Wissenschaft, Medizin, Umwelt und Bildung. ",
37 | "pubDate": "Wed, 9 Oct 2019 16:39:00 +0200",
38 | "link": "https://www.swr.de/swr2/programm/SWR2-Wissen-Impuls-Podcast,swr2-impuls-podcast-100.html",
39 | "language": "de",
40 | "copyright": "Copyright © Südwestrundfunk",
41 | "itunes": {
42 | "owner": {
43 | "name": "Südwestrundfunk",
44 | "email": "podcast@swr.de"
45 | },
46 | "image": "https://www.swr.de/swr2/programm/Podcastbild-SWR2-Impuls,1564741489275,swr2-impuls-podcast-104~_v-1x1@2dXL_-1f32c27c4978132dd0854e53b5ed30e10facc189.jpg",
47 | "keywords": [
48 | "Gesundheit und Mensch",
49 | "Wissenschaft und Forschung",
50 | "Wissen",
51 | "Wissenschaft",
52 | "Impuls",
53 | "Podcast",
54 | "Download"
55 | ],
56 | "author": "SWR",
57 | "subtitle": "Neues aus Wissenschaft, Medizin, Umwelt und Bildung. Dazu Musik, die sich vom Mainstream abhebt. Montags bis freitags um 16:05. ",
58 | "summary": "Neues aus Wissenschaft, Medizin, Umwelt und Bildung. ",
59 | "explicit": "No"
60 | }
61 | }
62 | }
--------------------------------------------------------------------------------
/test/output/itunes-keywords.json:
--------------------------------------------------------------------------------
1 | {
2 | "feed": {
3 | "items": [
4 | {
5 | "creator": "Taverncast: www.taverncast.com",
6 | "title": "Taverncast 62 - Temporal Anomaly",
7 | "link": "http://taverncast.com/",
8 | "pubDate": "07 Nov 2015 12:00:00 EST",
9 | "author": "Taverncast",
10 | "content:encoded": "Taverncast tosses around the age old topic: time travel! Where would you go? Why would you go? Also, Bryce has customer service issues, and Mike buys a dog.",
11 | "content:encodedSnippet": "Taverncast tosses around the age old topic: time travel! Where would you go? Why would you go? Also, Bryce has customer service issues, and Mike buys a dog.",
12 | "enclosure": {
13 | "url": "http://www.podtrac.com/pts/redirect.mp3?http://www.taverncast.com/shows/taverncast-62.mp3",
14 | "length": "47160424",
15 | "type": "audio/mpeg"
16 | },
17 | "dc:creator": "Taverncast: www.taverncast.com",
18 | "comments": "http://www.taverncast.com",
19 | "content": "Taverncast tosses around the age old topic: time travel! Where would you go? Why would you go? Also, Bryce has customer service issues, and Mike buys a dog.",
20 | "contentSnippet": "Taverncast tosses around the age old topic: time travel! Where would you go? Why would you go? Also, Bryce has customer service issues, and Mike buys a dog.",
21 | "guid": "http://taverncast.com/shows/taverncast-62.mp3",
22 | "categories": [
23 | "Society & Culture"
24 | ],
25 | "isoDate": "2015-11-07T17:00:00.000Z",
26 | "itunes": {
27 | "author": "Taverncast",
28 | "subtitle": "Taverncast tosses around the age old topic: time travel! Where would you go? Why would you go? Also, Bryce has customer service issues, and Mike buys a dog.",
29 | "summary": "Taverncast tosses around the age old topic: time travel! Where would you go? Why would you go? Also, Bryce has customer service issues, and Mike buys a dog.",
30 | "explicit": "no",
31 | "duration": "1:09:50",
32 | "keywords": "Taverncast, beer, games, hobbies, gaming, computer, geek, party, movies, comics, pop-culture, weird, comedy, humor, funny, talk, nerd, news, politics, paranormal, conspiracy, morning show, stone brewing company, time travel, back to the future, past, future, present, timeline, temporal, travel, time, dog, doodle, poodle, apple, laptop, computer, arrogant, bastard, arrogant bastard"
33 | }
34 | }
35 | ],
36 | "image": {
37 | "link": "http://www.taverncast.com/",
38 | "url": "http://www.taverncast.com/itunes-logo.png",
39 | "title": "Taverncast - Happy Hour in Your Head - Since 2005",
40 | "width": "144",
41 | "height": "300"
42 | },
43 | "title": "Taverncast - Happy Hour in Your Head - Since 2005",
44 | "description": "Order a beer, pull up a seat and join your friends at Taverncast for our patented un-scripted roundtable discussions, covering a wide range of topics with a unique wit and inviting style - all blended together, it's Happy Hour in Your Head!",
45 | "pubDate": "Wed, 03 Aug 2005 18:00:00 GMT",
46 | "managingEditor": "taverncast@taverncast.com",
47 | "generator": "me mitts",
48 | "link": "http://www.taverncast.com/",
49 | "language": "en-us",
50 | "copyright": "Taverncast, 2015",
51 | "itunes": {
52 | "owner": {
53 | "name": "Taverncast",
54 | "email": "taverncast@taverncast.com"
55 | },
56 | "keywords": [
57 | "Culture",
58 | "Society"
59 | ],
60 | "author": "Bryce Erwin, Bill Ticknor, Michelle O'Neill, Mike Monan, Aric Watson, Jennifer Albrecht, Lauren Hoban and Derek Chew",
61 | "subtitle": "Beer - Talk - Fun. It's Happy Hour in Your Head!",
62 | "summary": "Order a beer, pull up a seat and join your friends at Taverncast for our patented un-scripted roundtable discussions, covering a wide range of topics with a unique wit and inviting style - all blended together, it's Happy Hour in Your Head!",
63 | "explicit": "no"
64 | }
65 | }
66 | }
--------------------------------------------------------------------------------
/test/output/missing-fields.json:
--------------------------------------------------------------------------------
1 | {
2 | "feed": {
3 | "items": [
4 | {
5 | "id": "tag:github.com,2008:Repository/11167738/v3.9.0"
6 | }
7 | ]
8 | }
9 | }
--------------------------------------------------------------------------------
/test/output/narro.json:
--------------------------------------------------------------------------------
1 | {
2 | "feed": {
3 | "items": [
4 | {
5 | "creator": "foobar@gmail.com",
6 | "title": "FAQ for Narro",
7 | "link": "https://www.narro.co/article/54e703933058540300000069",
8 | "pubDate": "Fri, 20 Feb 2015 09:51:15 UTC",
9 | "author": "foobar@gmail.com",
10 | "enclosure": {
11 | "url": "https://s3.amazonaws.com/nareta-articles/audio/54d046c293f79c0300000003/7e2d2b00-a945-441a-f49b-063786a319a4.mp3",
12 | "length": "74",
13 | "type": "audio/mpeg"
14 | },
15 | "content": "Listen to your reading list anywhere. Narro will take your bookmarked articles and read them back to you as a podcast. http://www.narro.co/faq ",
16 | "contentSnippet": "Listen to your reading list anywhere. Narro will take your bookmarked articles and read them back to you as a podcast.\n http://www.narro.co/faq",
17 | "guid": "https://www.narro.co/article/54e703933058540300000069",
18 | "isoDate": "2015-02-20T09:51:15.000Z",
19 | "itunes": {
20 | "author": "foobar@gmail.com",
21 | "subtitle": "FAQ for Narro",
22 | "summary": "Listen to your reading list anywhere. Narro will take your bookmarked articles and read them back to you as a podcast. ... http://www.narro.co/faq ... ",
23 | "explicit": "no",
24 | "duration": "74",
25 | "image": "http://example.com/someImage.jpg"
26 | }
27 | }
28 | ],
29 | "feedUrl": "http://on.narro.co/f",
30 | "paginationLinks": {
31 | "self": "http://on.narro.co/f"
32 | },
33 | "title": "foobar on Narro",
34 | "description": "foobar uses Narro to create a podcast of articles transcribed to audio.",
35 | "pubDate": "Fri, 08 Jul 2016 13:40:00 UTC",
36 | "webMaster": "josh@narro.co",
37 | "managingEditor": "foobar@gmail.com",
38 | "generator": "gopod - http://github.com/jbckmn/gopod",
39 | "link": "http://on.narro.co/f",
40 | "language": "en",
41 | "copyright": "All article content copyright of respective source authors.",
42 | "ttl": "20",
43 | "itunes": {
44 | "owner": {
45 | "name": "foobar",
46 | "email": "foobar@gmail.com"
47 | },
48 | "image": "https://www.narro.co/images/narro-icon-lg.png",
49 | "author": "foobar@gmail.com",
50 | "subtitle": "foobar uses Narro to create a podcast of articles transcribed to audio.",
51 | "summary": "foobar uses Narro to create a podcast of articles transcribed to audio.",
52 | "explicit": "no"
53 | }
54 | }
55 | }
--------------------------------------------------------------------------------
/test/output/pagination-links.json:
--------------------------------------------------------------------------------
1 | {
2 | "feed": {
3 | "items": [
4 | {
5 | "title": "The First Episode",
6 | "link": "https://example.test/episode?id=283843",
7 | "pubDate": "Thu, 21 Jan 2021 18:58:00 +1100",
8 | "enclosure": {
9 | "url": "https://example.test/test-audio.mp3",
10 | "length": "38068096",
11 | "type": "audio/mpeg"
12 | },
13 | "content": "The First Episode
",
14 | "contentSnippet": "The First Episode",
15 | "guid": "a6f22abe-be5c-4a37-8f4b-8be3d69b0235",
16 | "isoDate": "2021-01-21T07:58:00.000Z",
17 | "itunes": {
18 | "author": "Steve Thompson",
19 | "subtitle": "The First Episode...",
20 | "summary": "The First Episode",
21 | "explicit": "No",
22 | "duration": "39:27",
23 | "image": "https://example.test/test-image.jpg"
24 | }
25 | }
26 | ],
27 | "feedUrl": "http://example.org/index.atom?page=3",
28 | "image": {
29 | "link": "https://example.test/shows/new-show-1",
30 | "url": "https://example.test/test-image.jpg",
31 | "title": "New Show"
32 | },
33 | "paginationLinks": {
34 | "self": "http://example.org/index.atom?page=3",
35 | "first": "http://example.org/index.atom",
36 | "next": "http://example.org/index.atom?page=4",
37 | "prev": "http://example.org/index.atom?page=2",
38 | "last": "http://example.org/index.atom?page=5"
39 | },
40 | "title": "New Show - With Episode Block",
41 | "description": "New Show",
42 | "link": "https://example.test/shows/new-show-1",
43 | "language": "en-us",
44 | "lastBuildDate": "Tue, 31 Mar 2020 05:19:30 +0000",
45 | "itunes": {
46 | "owner": {
47 | "name": "Steve Thompson",
48 | "email": "steve.t@gmail.com"
49 | },
50 | "image": "https://example.test/test-image.jpg",
51 | "categories": [
52 | "Arts"
53 | ],
54 | "categoriesWithSubs": [
55 | {
56 | "name": "Arts",
57 | "subs": null
58 | }
59 | ],
60 | "author": "Steve Thompson",
61 | "subtitle": "New Show",
62 | "summary": "New Show",
63 | "explicit": "No"
64 | }
65 | }
66 | }
--------------------------------------------------------------------------------
/test/output/uolNoticias.json:
--------------------------------------------------------------------------------
1 | {
2 | "feed": {
3 | "items": [
4 | {
5 | "title": "Ibope: Bolsonaro perde de Haddad, Ciro e Alckmin em simula��es de 2� turno",
6 | "link": "https://noticias.uol.com.br/politica/eleicoes/2018/noticias/2018/09/24/ibope-bolsonaro-perde-de-haddad-ciro-e-alckmin-em-simulacoes-de-2-turno.htm",
7 | "pubDate": "Seg, 24 Set 2018 19:42:40 -0300",
8 | "content": " \tL�der na pesquisa Ibope de inten��o de voto para o primeiro turno divulgada nesta segunda-feira (24), o candidato do PSL a presidente, Jair Bolsonaro, n�o tem o mesmo desempenho nas simula��es de segundo turno feitas pela empresa no mesmo levantamento. ",
9 | "contentSnippet": "L�der na pesquisa Ibope de inten��o de voto para o primeiro turno divulgada nesta segunda-feira (24), o candidato do PSL a presidente, Jair Bolsonaro, n�o tem o mesmo desempenho nas simula��es de segundo turno feitas pela empresa no mesmo levantamento."
10 | },
11 | {
12 | "title": "Promotoria abre inqu�rito para apurar suspeita de improbidade de Skaf no Sebrae-SP",
13 | "link": "https://www1.folha.uol.com.br/poder/2018/09/promotoria-abre-inquerito-para-apurar-suspeita-de-improbidade-de-skaf-no-sebrae-sp.shtml",
14 | "pubDate": "Seg, 24 Set 2018 19:38:00 -0300",
15 | "content": "",
16 | "contentSnippet": ""
17 | },
18 | {
19 | "title": "Apucarana garante continuidade do Pr� Aprendiz; ano vai fechar�com 13 mil pessoas qualificadas",
20 | "link": "https://tnonline.uol.com.br/noticias/apucarana/45,470927,24,09,apucarana-garante-continuidade-do-pre-aprendiz-ano-vai-fechar-com-13-mil-pessoas-qualificadas",
21 | "pubDate": "Seg, 24 Set 2018 19:35:07 -0300",
22 | "content": " Programa Pr� Aprendiz, que � realizado pela Secretaria Municipal de Assist�ncia Social de Apucarana em parceria com o Servi�o Social do Com�rcio (Sesc), ter� continuidade em 2019.�O termo de renova�... ",
23 | "contentSnippet": "Programa Pr� Aprendiz, que � realizado pela Secretaria Municipal de Assist�ncia Social de Apucarana em parceria com o Servi�o Social do Com�rcio (Sesc), ter� continuidade em 2019.�O termo de renova�..."
24 | },
25 | {
26 | "title": "Dow Jones fecha em baixa de 0,68%",
27 | "link": "https://economia.uol.com.br/noticias/efe/2018/09/24/dow-jones-fecha-em-baixa-de-068.htm",
28 | "pubDate": "Seg, 24 Set 2018 19:32:00 -0300",
29 | "content": " Nova York, 24 set (EFE).- O �ndice Dow Jones Industrial fechou nesta segunda-feira em baixa de 0,68% em mais um preg�o marcado pela disputa comercial entre Estados Unidos e China e por especula��es sobre a poss�vel ren�ncia de Rod Rosenstein ao cargo de procurador-geral adjunto dos Estados Unidos. ",
30 | "contentSnippet": "Nova York, 24 set (EFE).- O �ndice Dow Jones Industrial fechou nesta segunda-feira em baixa de 0,68% em mais um preg�o marcado pela disputa comercial entre Estados Unidos e China e por especula��es sobre a poss�vel ren�ncia de Rod Rosenstein ao cargo de procurador-geral adjunto dos Estados Unidos."
31 | },
32 | {
33 | "title": "Rival de Putin � condenado a mais 20 dias de pris�o",
34 | "link": "https://noticias.uol.com.br/ultimas-noticias/ansa/2018/09/24/rival-de-putin-e-condenado-a-mais-20-dias-de-prisao.htm",
35 | "pubDate": "Seg, 24 Set 2018 19:32:00 -0300",
36 | "content": " MOSCOU, 24 SET (ANSA) - O l�der de oposi��o russo Alexei Navalny foi condenado na noite desta segunda-feira (24) a mais 20 dias de pris�o por organizar manifesta��es n�o autorizadas contra o governo de Vladimir Putin, informou a m�dia russa. Navalny foi preso nesta manh� pouco tempo depois de ser libertado da cadeia ap�s cumprir outra senten�a de 30 dias.��� ",
37 | "contentSnippet": "MOSCOU, 24 SET (ANSA) - O l�der de oposi��o russo Alexei Navalny foi condenado na noite desta segunda-feira (24) a mais 20 dias de pris�o por organizar manifesta��es n�o autorizadas contra o governo de Vladimir Putin, informou a m�dia russa. Navalny foi preso nesta manh� pouco tempo depois de ser libertado da cadeia ap�s cumprir outra senten�a de 30 dias.���"
38 | },
39 | {
40 | "title": "Em entrevista, Bolsonaro chama proposta de Paulo Guedes para imposto de renda de \"ousada\"",
41 | "link": "https://noticias.uol.com.br/ultimas-noticias/reuters/2018/09/24/em-entrevista-bolsonaro-chama-proposta-de-paulo-guedes-para-imposto-de-renda-de-ousada.htm",
42 | "pubDate": "Seg, 24 Set 2018 19:30:42 -0300",
43 | "content": " BRAS�LIA (Reuters) - O candidato do PSL � Presid�ncia, Jair Bolsonaro, classificou nesta segunda-feira a proposta do seu principal assessor econ�mico, Paulo Guedes, para a mudan�a na forma de cobran�a do imposto de renda para pessoa f�sica de \"ousada\". ",
44 | "contentSnippet": "BRAS�LIA (Reuters) - O candidato do PSL � Presid�ncia, Jair Bolsonaro, classificou nesta segunda-feira a proposta do seu principal assessor econ�mico, Paulo Guedes, para a mudan�a na forma de cobran�a do imposto de renda para pessoa f�sica de \"ousada\"."
45 | },
46 | {
47 | "title": "Relat�rio do governo dos EUA acusa militares de Mianmar de atrocidades contra mu�ulmanos rohingyas",
48 | "link": "https://noticias.uol.com.br/ultimas-noticias/reuters/2018/09/24/relatorio-do-governo-dos-eua-acusa-militares-de-mianmar-de-atrocidades-contra-muculmanos-rohingyas.htm",
49 | "pubDate": "Seg, 24 Set 2018 19:29:18 -0300",
50 | "content": " Por Matt Spetalnick e Jason Szep ",
51 | "contentSnippet": "Por Matt Spetalnick e Jason Szep"
52 | },
53 | {
54 | "title": "Enfermeira de Ch�vez alega 'persegui��o' em Corte espanhola",
55 | "link": "https://noticias.uol.com.br/ultimas-noticias/ansa/2018/09/24/enfermeira-de-chavez-alega-perseguicao-em-corte-espanhola.htm",
56 | "pubDate": "Seg, 24 Set 2018 19:25:00 -0300",
57 | "content": " MADRI, 24 SET (ANSA) - A ex-enfermeira do falecido Hugo Ch�vez, ex-presidente venezuelano, prestou depoimento hoje (24) na Corte Nacional da Espanha e alegou sofrer persegui�... ",
58 | "contentSnippet": "MADRI, 24 SET (ANSA) - A ex-enfermeira do falecido Hugo Ch�vez, ex-presidente venezuelano, prestou depoimento hoje (24) na Corte Nacional da Espanha e alegou sofrer persegui�..."
59 | },
60 | {
61 | "title": "Match Eleitoral j� soma mais de 500 mil testes completos; ache seu candidato",
62 | "link": "https://www1.folha.uol.com.br/poder/2018/09/match-eleitoral-ja-soma-mais-de-500-mil-testes-completos-ache-seu-candidato.shtml",
63 | "pubDate": "Seg, 24 Set 2018 19:22:00 -0300",
64 | "content": "",
65 | "contentSnippet": ""
66 | },
67 | {
68 | "title": "Match Eleitoral j� soma mais de 500 mil testes completos; ache seu candidato",
69 | "link": "https://redir.folha.com.br/redir/online/poder/eleicoes-2018/rss091/*https://www1.folha.uol.com.br/poder/2018/09/match-eleitoral-ja-soma-mais-de-500-mil-testes-completos-ache-seu-candidato.shtml",
70 | "pubDate": "Seg, 24 Set 2018 19:22:00 -0300",
71 | "content": " J� s�o mais de 500 mil testes completos no�Match Eleitoral, ferramenta criada pela�Folha�e pelo Datafolha para ajudar o�eleitor a escolher�seu deputado federal por S�o Paulo, Minas Gerais e Rio de Janeiro, al�m de senadores por S�o Paulo.�Leia mais (09/24/2018 - 19h22) ",
72 | "contentSnippet": "J� s�o mais de 500 mil testes completos no�Match Eleitoral, ferramenta criada pela�Folha�e pelo Datafolha para ajudar o�eleitor a escolher�seu deputado federal por S�o Paulo, Minas Gerais e Rio de Janeiro, al�m de senadores por S�o Paulo.�Leia mais (09/24/2018 - 19h22)"
73 | },
74 | {
75 | "title": "Acordo adicional da Argentina com FMI est� \"perto de acontecer\", diz Macri",
76 | "link": "https://www1.folha.uol.com.br/mercado/2018/09/acordo-adicional-da-argentina-com-fmi-esta-perto-de-acontecer-diz-macri.shtml",
77 | "pubDate": "Seg, 24 Set 2018 19:22:00 -0300",
78 | "content": "",
79 | "contentSnippet": ""
80 | },
81 | {
82 | "title": "Fernanda Melchionna: a Primavera Feminista vai derrotar Bolsonaro nas ruas e nas urnas!",
83 | "link": "https://agoraequesaoelas.blogfolha.uol.com.br/?p=1609",
84 | "pubDate": "Seg, 24 Set 2018 19:21:00 -0300",
85 | "content": "",
86 | "contentSnippet": ""
87 | },
88 | {
89 | "title": "Ibope: Bolsonaro estaciona na lideran�a; Haddad segue avan�ando",
90 | "link": "https://congressoemfoco.uol.com.br/eleicoes/ibope-bolsonaro-estaciona-na-lideranca-haddad-segue-avancando/",
91 | "pubDate": "Seg, 24 Set 2018 19:20:08 -0300",
92 | "content": " Em nova pesquisa Ibope divulgada nesta segunda-feira (24), o candidato do PSL � Presid�ncia, Jair Bolsonaro, continua l�der com 28% das inten��es de voto, sem apresentar crescimento em rela��o � �ltima pesquisa, quando teve a mesma pontua��o. O candidato do PT, Fernando Haddad, aproxima-se de Bolsonaro com 22%, uma diferen�a de 3 pontos percentuais em [?] ",
93 | "contentSnippet": "Em nova pesquisa Ibope divulgada nesta segunda-feira (24), o candidato do PSL � Presid�ncia, Jair Bolsonaro, continua l�der com 28% das inten��es de voto, sem apresentar crescimento em rela��o � �ltima pesquisa, quando teve a mesma pontua��o. O candidato do PT, Fernando Haddad, aproxima-se de Bolsonaro com 22%, uma diferen�a de 3 pontos percentuais em [?]"
94 | },
95 | {
96 | "title": "Assange chegou a renunciar a asilo do Equador, segundo carta privada",
97 | "link": "https://noticias.uol.com.br/ultimas-noticias/afp/2018/09/24/assange-chegou-a-renunciar-a-asilo-do-equador-segundo-carta-privada.htm",
98 | "pubDate": "Seg, 24 Set 2018 19:20:00 -0300",
99 | "content": " Quito, 24 Set 2018 (AFP) - O fundador do WikiLeaks, Julian Assange, refugiado na embaixada equatoriana em Londres h� seis anos, chegou a renunciar ao asilo concedido por Quito, segundo uma carta assinada por ele em dezembro passado � qual a AFP teve acesso. ",
100 | "contentSnippet": "Quito, 24 Set 2018 (AFP) - O fundador do WikiLeaks, Julian Assange, refugiado na embaixada equatoriana em Londres h� seis anos, chegou a renunciar ao asilo concedido por Quito, segundo uma carta assinada por ele em dezembro passado � qual a AFP teve acesso."
101 | },
102 | {
103 | "title": "Fama \"A\" � campe� da Primeira Divis�o da Copa Cidade Alta de Futebol Su��o",
104 | "link": "https://tnonline.uol.com.br/noticias/apucarana/45,470926,24,09,fama-a-e-campea-da-primeira-divisao-da-copa-cidade-alta-de-futebol-suico",
105 | "pubDate": "Seg, 24 Set 2018 19:18:49 -0300",
106 | "content": " om a Arena Fama lotada, a equipe da Molas Fama/Multividros \"A\" foi campe� neste s�bado da Primeira Divis�o da 10� edi��o da Copa Cidade Alta de Futebol Su��o, categoria livre, ao vencer a Estamparia ... ",
107 | "contentSnippet": "om a Arena Fama lotada, a equipe da Molas Fama/Multividros \"A\" foi campe� neste s�bado da Primeira Divis�o da 10� edi��o da Copa Cidade Alta de Futebol Su��o, categoria livre, ao vencer a Estamparia ..."
108 | }
109 | ],
110 | "image": {
111 | "link": "http://noticias.uol.com.br/ultimas/",
112 | "url": "http://rss.i.uol.com.br/uol_rss.gif",
113 | "title": "UOL Noticias - �ltimas Not�cias"
114 | },
115 | "title": "UOL Noticias",
116 | "description": "�ltimas Not�cias",
117 | "link": "http://noticias.uol.com.br/",
118 | "language": "",
119 | "copyright": ""
120 | }
121 | }
--------------------------------------------------------------------------------
/test/parser.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var fs = require('fs');
4 | var HTTP = require('http');
5 |
6 | var Parser = require('../index.js');
7 |
8 | var Expect = require('chai').expect;
9 |
10 | var IN_DIR = __dirname + '/input';
11 | var OUT_DIR = __dirname + '/output';
12 |
13 | describe('Parser', function() {
14 | var testParseForFile = function(name, ext, options, done) {
15 | if (typeof done === 'undefined') {
16 | done = options;
17 | options = {};
18 | }
19 | let parser = new Parser(options);
20 | let xml = fs.readFileSync(IN_DIR + '/' + name + '.' + ext, 'utf8');
21 | parser.parseString(xml, function(err, parsed) {
22 | if (err) console.log(err);
23 | Expect(err).to.equal(null);
24 | if (process.env.WRITE_GOLDEN) {
25 | fs.writeFileSync(OUT_DIR + '/' + name + '.json', JSON.stringify({feed: parsed}, null, 2));
26 | } else {
27 | var expected = fs.readFileSync(OUT_DIR + '/' + name + '.json', 'utf8')
28 | expected = JSON.parse(expected);
29 | Expect({feed: parsed}).to.deep.equal(expected);
30 | }
31 | done();
32 | })
33 | }
34 |
35 | it('should parse Reddit', function(done) {
36 | testParseForFile('reddit', 'rss', done);
37 | })
38 |
39 | it('should parse sciencemag.org (RSS 1.0)', function(done) {
40 | testParseForFile('rss-1', 'rss', done);
41 | })
42 |
43 | it('should parse craigslist (RSS 1.0)', function(done) {
44 | testParseForFile('craigslist', 'rss', done);
45 | })
46 |
47 | it('should parse atom', function(done) {
48 | testParseForFile('reddit-atom', 'rss', done);
49 | })
50 |
51 | it('should parse atom feed', function(done) {
52 | testParseForFile('gulp-atom', 'atom', done);
53 | })
54 |
55 | it('should parse reddits new feed', function(done) {
56 | testParseForFile('reddit-home', 'rss', done);
57 | })
58 |
59 | it('should parse with missing fields', function(done) {
60 | testParseForFile('missing-fields', 'atom', done)
61 | })
62 |
63 | it('should parse with incomplete fields', function(done) {
64 | testParseForFile('incomplete-fields', 'atom', done)
65 | })
66 |
67 | it('should parse heise', function(done) {
68 | testParseForFile('heise', 'atom', done);
69 | })
70 |
71 | it('should parse heraldsun', function(done) {
72 | testParseForFile('heraldsun', 'rss', done);
73 | });
74 |
75 | it('should parse UOL Noticias', function(done) {
76 | testParseForFile('uolNoticias', 'rss', { defaultRSS: 2.0 }, done);
77 | });
78 |
79 | it('should NOT parse UOL Noticias, if no default RSS is provided', function(done) {
80 | function willFail() {
81 | testParseForFile('uolNoticias', 'rss', done);
82 | }
83 | Expect(willFail).to.throw;
84 | done();
85 | });
86 |
87 | it('should parse Instant Article', function(done) {
88 | testParseForFile('instant-article', 'rss', done);
89 | });
90 |
91 | it('should parse Feedburner', function(done) {
92 | testParseForFile('feedburner', 'atom', done);
93 | });
94 |
95 | it('should parse podcasts', function(done) {
96 | testParseForFile('narro', 'rss', done);
97 | });
98 |
99 | it('should parse multiple links', function(done) {
100 | testParseForFile('many-links', 'rss', done);
101 | });
102 |
103 | it('should parse itunes with empty href', function(done) {
104 | testParseForFile('itunes-href', 'rss', done);
105 | });
106 |
107 | it('should pass xml2js options', function(done) {
108 | testParseForFile('xml2js-options', 'rss', {xml2js: {emptyTag: 'EMPTY'}}, done);
109 | });
110 |
111 | it('should throw error for unrecognized', function(done) {
112 | let parser = new Parser();
113 | let xml = fs.readFileSync(__dirname + '/input/unrecognized.rss', 'utf8');
114 | parser.parseString(xml, function(err, parsed) {
115 | Expect(err.message).to.contain('Feed not recognized as RSS');
116 | done();
117 | });
118 | });
119 |
120 | it('should omit iTunes image if none available during decoration', function(done) {
121 | const rssFeedWithMissingImage = __dirname + '/input/itunes-missing-image.rss';
122 | const xml = fs.readFileSync(rssFeedWithMissingImage, 'utf8');
123 | let parser = new Parser();
124 | parser.parseString(xml, function(err, parsed) {
125 | Expect(err).to.be.null;
126 | Expect(parsed).to.not.have.deep.property('feed.itunes.image');
127 | done();
128 | });
129 | });
130 |
131 | it('should parse custom fields', function(done) {
132 | var options = {
133 | customFields: {
134 | feed: ['language', 'copyright', 'nested-field'],
135 | item: ['subtitle']
136 | }
137 | };
138 | testParseForFile('customfields', 'rss', options, done);
139 | });
140 |
141 | it('should parse Atom feed custom fields', function(done) {
142 | var options = {
143 | customFields: {
144 | feed: ['totalViews'],
145 | item: ['media:group']
146 | }
147 | };
148 | testParseForFile('atom-customfields', 'atom', options, done);
149 | });
150 |
151 | it('should parse sibling custom fields', function(done) {
152 | var options = {
153 | customFields: {
154 | item: [['media:content', 'media:content', {keepArray: true}]]
155 | }
156 | };
157 | testParseForFile('guardian', 'rss', options, done);
158 | });
159 |
160 | it('should parse URL', function(done) {
161 | var INPUT_FILE = __dirname + '/input/reddit.rss';
162 | var OUTPUT_FILE = __dirname + '/output/reddit.json';
163 | var server = HTTP.createServer(function(req, res) {
164 | var file = fs.createReadStream(INPUT_FILE, 'utf8');
165 | file.pipe(res);
166 | });
167 | server.listen(function() {
168 | var port = server.address().port;
169 | var url = 'http://localhost:' + port;
170 | let parser = new Parser();
171 | parser.parseURL(url, function(err, parsed) {
172 | Expect(err).to.equal(null);
173 | if (process.env.WRITE_GOLDEN) {
174 | fs.writeFileSync(OUTPUT_FILE, JSON.stringify({feed: parsed}, null, 2));
175 | } else {
176 | var expected = JSON.parse(fs.readFileSync(OUTPUT_FILE, 'utf8'));
177 | Expect({feed: parsed}).to.deep.equal(expected);
178 | }
179 | server.close();
180 | done();
181 | });
182 | });
183 | });
184 |
185 | it('should parse URL with relative redirect', function(done) {
186 | var INPUT_FILE = __dirname + '/input/reddit.rss';
187 | var OUTPUT_FILE = __dirname + '/output/reddit.json';
188 | var server = HTTP.createServer(function(req, res) {
189 | if (req.url !== '/new-location') {
190 | res.writeHead(301, { 'Location': '/new-location'});
191 | res.end();
192 | } else {
193 | var file = fs.createReadStream(INPUT_FILE, 'utf8');
194 | file.pipe(res);
195 | }
196 | });
197 | server.listen(function() {
198 | var port = server.address().port;
199 | var url = 'http://localhost:' + port;
200 | let parser = new Parser();
201 | parser.parseURL(url, function(err, parsed) {
202 | Expect(err).to.equal(null);
203 | if (process.env.WRITE_GOLDEN) {
204 | fs.writeFileSync(OUTPUT_FILE, JSON.stringify({feed: parsed}, null, 2));
205 | } else {
206 | var expected = JSON.parse(fs.readFileSync(OUTPUT_FILE, 'utf8'));
207 | Expect({feed: parsed}).to.deep.equal(expected);
208 | }
209 | server.close();
210 | done();
211 | });
212 | });
213 | });
214 |
215 | it('should use proper encoding', function(done) {
216 | var INPUT_FILE = __dirname + '/input/encoding.rss';
217 | var OUTPUT_FILE = __dirname + '/output/encoding.json';
218 | var ENCODING = 'latin1';
219 | var server = HTTP.createServer(function(req, res) {
220 | res.setHeader('Content-Type', 'text/xml; charset=' + ENCODING)
221 | var file = fs.readFileSync(INPUT_FILE, ENCODING);
222 | res.end(file, ENCODING);
223 | });
224 | server.listen(function() {
225 | var port = server.address().port;
226 | var url = 'http://localhost:' + port;
227 | var parser = new Parser();
228 | parser.parseURL(url, function(err, parsed) {
229 | Expect(err).to.equal(null);
230 | if (process.env.WRITE_GOLDEN) {
231 | fs.writeFileSync(OUTPUT_FILE, JSON.stringify({feed: parsed}, null, 2), {encoding: ENCODING});
232 | } else {
233 | var expected = JSON.parse(fs.readFileSync(OUTPUT_FILE, ENCODING));
234 | Expect({feed: parsed}).to.deep.equal(expected);
235 | }
236 | server.close();
237 | done();
238 | })
239 | })
240 | });
241 |
242 | it('should respect timeout option', function(done) {
243 | var server = HTTP.createServer(function(req, res) {});
244 | server.listen(function() {
245 | var port = server.address().port;
246 | var url = 'http://localhost:' + port;
247 | var parser = new Parser({timeout: 1});
248 | parser.parseURL(url, function(err, parsed) {
249 | Expect(err).to.not.equal(null);
250 | Expect(err.message).to.equal("Request timed out after 1ms");
251 | done();
252 | });
253 | });
254 | });
255 |
256 | it('should parse episodeType', function(done) {
257 | testParseForFile('item-itunes-episodeType', 'rss', done);
258 | });
259 |
260 | it('should parse itunes categories', function(done) {
261 | testParseForFile('itunes-category', 'rss', done);
262 | });
263 |
264 | it('should parse itunes keywords', function(done) {
265 | testParseForFile('itunes-keywords', 'rss', done);
266 | });
267 |
268 | it('should parse itunes keywords as array', function(done) {
269 | testParseForFile('itunes-keywords-array', 'rss', done);
270 | });
271 |
272 | it('should parse itunes keywords with attribute `text`', function(done) {
273 | testParseForFile('itunes-keywords-astext', 'rss', done);
274 | });
275 |
276 | it('should parse giantbomb-podcast', function(done) {
277 | testParseForFile('giantbomb-podcast', 'rss', done);
278 | });
279 |
280 | it('should parse content:encoded', function(done) {
281 | testParseForFile('content-encoded', 'rss', done);
282 | });
283 |
284 | it('should parse atom:link pagination links', function (done) {
285 | testParseForFile('pagination-links', 'rss', done);
286 | });
287 | })
288 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var webpack = require("webpack");
2 | module.exports = {
3 | entry: {
4 | "rss-parser": "./index.js"
5 | },
6 | output: {
7 | path: __dirname,
8 | filename: "dist/[name].js",
9 | libraryTarget: 'umd',
10 | globalObject: 'this',
11 | library: 'RSSParser'
12 | },
13 | resolve: {
14 | extensions: ['.js']
15 | },
16 | devtool: 'source-map',
17 | module: {
18 | rules: [{
19 | test: /\.js$/,
20 | use: [{
21 | loader: 'babel-loader',
22 | options: {presets: ['@babel/preset-env']},
23 | }],
24 | }]
25 | },
26 | externals: {
27 | xmlbuilder:'xmlbuilder'
28 | },
29 | resolve: {
30 | fallback: {
31 | "https": false,
32 | "http": false,
33 | "url": false,
34 | "stream": false,
35 | "fs": false,
36 | "timers": false,
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------