Browse Source

implements parsing Atom and RSS feeds

pull/49/head
Aaron Parecki 6 years ago
parent
commit
e8e63caba6
No known key found for this signature in database GPG Key ID: 276C2817346D6056
10 changed files with 1006 additions and 29 deletions
  1. +2
    -1
      composer.json
  2. +99
    -1
      composer.lock
  3. +4
    -0
      lib/XRay/Formats/Format.php
  4. +3
    -9
      lib/XRay/Formats/Mf2.php
  5. +92
    -0
      lib/XRay/Formats/XML.php
  6. +4
    -0
      lib/XRay/Parser.php
  7. +107
    -18
      tests/FeedTest.php
  8. +201
    -0
      tests/data/feed.example.com/atom
  9. +297
    -0
      tests/data/feed.example.com/podcast-rss
  10. +197
    -0
      tests/data/feed.example.com/rss

+ 2
- 1
composer.json View File

@ -11,7 +11,8 @@
"dg/twitter-php": "3.6.*", "dg/twitter-php": "3.6.*",
"p3k/timezone": "*", "p3k/timezone": "*",
"p3k/http": "0.1.*", "p3k/http": "0.1.*",
"cebe/markdown": "1.1.*"
"cebe/markdown": "1.1.*",
"miniflux/picofeed": "^0.1.37"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {

+ 99
- 1
composer.lock View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "082e2019c20a5d611f58716d3f75a131",
"content-hash": "4cacb7d73963baec7b3fbc3248e43833",
"packages": [ "packages": [
{ {
"name": "cebe/markdown", "name": "cebe/markdown",
@ -256,6 +256,59 @@
], ],
"time": "2017-05-27T15:27:47+00:00" "time": "2017-05-27T15:27:47+00:00"
}, },
{
"name": "miniflux/picofeed",
"version": "v0.1.37",
"source": {
"type": "git",
"url": "https://github.com/miniflux/picoFeed.git",
"reference": "402b7f07629577e7929625e78bc88d3d5831a22d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/miniflux/picoFeed/zipball/402b7f07629577e7929625e78bc88d3d5831a22d",
"reference": "402b7f07629577e7929625e78bc88d3d5831a22d",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-iconv": "*",
"ext-libxml": "*",
"ext-simplexml": "*",
"ext-xml": "*",
"php": ">=5.3.0",
"zendframework/zendxml": "^1.0"
},
"require-dev": {
"phpdocumentor/reflection-docblock": "2.0.4",
"phpunit/phpunit": "4.8.26",
"symfony/yaml": "2.8.7"
},
"suggest": {
"ext-curl": "PicoFeed will use cURL if present"
},
"bin": [
"picofeed"
],
"type": "library",
"autoload": {
"psr-0": {
"PicoFeed": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Frédéric Guillot"
}
],
"description": "Modern library to handle RSS/Atom feeds",
"homepage": "https://github.com/miniflux/picoFeed",
"time": "2017-11-02T03:20:36+00:00"
},
{ {
"name": "p3k/http", "name": "p3k/http",
"version": "0.1.5", "version": "0.1.5",
@ -328,6 +381,51 @@
"timezone" "timezone"
], ],
"time": "2017-01-12T17:30:08+00:00" "time": "2017-01-12T17:30:08+00:00"
},
{
"name": "zendframework/zendxml",
"version": "1.0.2",
"source": {
"type": "git",
"url": "https://github.com/zendframework/ZendXml.git",
"reference": "7b64507bc35d841c9c5802d67f6f87ef8e1a58c9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/ZendXml/zipball/7b64507bc35d841c9c5802d67f6f87ef8e1a58c9",
"reference": "7b64507bc35d841c9c5802d67f6f87ef8e1a58c9",
"shasum": ""
},
"require": {
"php": "^5.3.3 || ^7.0"
},
"require-dev": {
"phpunit/phpunit": "^3.7 || ^4.0",
"squizlabs/php_codesniffer": "^1.5"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"autoload": {
"psr-0": {
"ZendXml\\": "library/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"description": "Utility library for XML usage, best practices, and security in PHP",
"homepage": "http://packages.zendframework.com/",
"keywords": [
"security",
"xml",
"zf2"
],
"time": "2016-02-04T21:02:08+00:00"
} }
], ],
"packages-dev": [ "packages-dev": [

+ 4
- 0
lib/XRay/Formats/Format.php View File

@ -81,5 +81,9 @@ abstract class Format implements iFormat {
return $sanitized; return $sanitized;
} }
protected static function stripHTML($html) {
return trim(strip_tags($html));
}
} }

+ 3
- 9
lib/XRay/Formats/Mf2.php View File

@ -535,18 +535,12 @@ class Mf2 extends Format {
private static function parseAsHFeed($mf2, $http) { private static function parseAsHFeed($mf2, $http) {
$data = [ $data = [
'type' => 'feed', 'type' => 'feed',
'author' => [
'type' => 'card',
'name' => null,
'url' => null,
'photo' => null
],
'todo' => 'Not yet implemented. Please see https://github.com/aaronpk/XRay/issues/1'
'todo' => 'Not yet implemented. Please see https://github.com/aaronpk/XRay/issues/1',
'items' => [],
]; ];
return [ return [
'data' => $data,
'entries' => []
'data' => $data
]; ];
} }

+ 92
- 0
lib/XRay/Formats/XML.php View File

@ -0,0 +1,92 @@
<?php
namespace p3k\XRay\Formats;
use HTMLPurifier, HTMLPurifier_Config;
use DOMDocument, DOMXPath;
use p3k\XRay\Formats;
use PicoFeed\Reader\Reader;
use PicoFeed\PicoFeedException;
class XML extends Format {
public static function matches_host($url) { return true; }
public static function matches($url) { return true; }
public static function parse($xml, $url) {
$result = [
'data' => [
'type' => 'unknown',
],
'url' => $url,
];
try {
$reader = new Reader();
$parser = $reader->getParser($url, $xml, '');
$feed = $parser->execute();
$result['data']['type'] = 'feed';
$result['data']['items'] = [];
foreach($feed->getItems() as $item) {
$result['data']['items'][] = self::_hEntryFromFeedItem($item, $feed);
}
} catch(PicoFeedException $e) {
}
return $result;
}
private static function _hEntryFromFeedItem($item, $feed) {
$entry = [
'type' => 'entry',
'author' => [
'name' => null,
'url' => null,
'photo' => null
]
];
if($item->getUrl())
$entry['url'] = $item->getUrl();
if($item->getPublishedDate())
$entry['published'] = $item->getPublishedDate()->format('c');
if($item->getTitle() && $item->getTitle() != $item->getUrl())
$entry['name'] = $item->getTitle();
if($item->getContent())
$entry['content'] = [
'html' => self::sanitizeHTML($item->getContent()),
'text' => self::stripHTML($item->getContent())
];
if($item->getAuthor()) {
$entry['author']['name'] = $item->getAuthor();
}
if($feed->siteUrl) {
$entry['author']['url'] = $feed->siteUrl;
}
if($item->getEnclosureType()) {
$prop = false;
switch($item->getEnclosureType()) {
case 'audio/mpeg':
$prop = 'audio'; break;
case 'image/jpeg':
case 'image/png':
case 'image/gif':
$prop = 'photo'; break;
}
if($prop)
$entry[$prop] = $item->getEnclosureUrl();
}
return $entry;
}
}

+ 4
- 0
lib/XRay/Parser.php View File

@ -38,6 +38,10 @@ class Parser {
return Formats\Hackernews::parse($body, $url); return Formats\Hackernews::parse($body, $url);
} }
if(substr($body, 0, 5) == '<?xml') {
return Formats\XML::parse($body, $url);
}
// No special parsers matched, parse for Microformats now // No special parsers matched, parse for Microformats now
return Formats\HTML::parse($this->http, $body, $url, $opts); return Formats\HTML::parse($this->http, $body, $url, $opts);
} }

+ 107
- 18
tests/FeedTest.php View File

@ -20,68 +20,157 @@ class FeedTest extends PHPUnit_Framework_TestCase {
public function testListOfHEntrys() { public function testListOfHEntrys() {
$url = 'http://feed.example.com/list-of-hentrys'; $url = 'http://feed.example.com/list-of-hentrys';
$response = $this->parse(['url' => $url]);
$response = $this->parse(['url' => $url, 'expect' => 'feed']);
$body = $response->getContent(); $body = $response->getContent();
$this->assertEquals(200, $response->getStatusCode()); $this->assertEquals(200, $response->getStatusCode());
$data = json_decode($body);
$data = json_decode($body)->data;
$this->assertEquals('feed', $data->data->type);
$this->assertEquals('feed', $data->type);
} }
public function testListOfHEntrysWithHCard() { public function testListOfHEntrysWithHCard() {
$url = 'http://feed.example.com/list-of-hentrys-with-h-card'; $url = 'http://feed.example.com/list-of-hentrys-with-h-card';
$response = $this->parse(['url' => $url]);
$response = $this->parse(['url' => $url, 'expect' => 'feed']);
$body = $response->getContent(); $body = $response->getContent();
$this->assertEquals(200, $response->getStatusCode()); $this->assertEquals(200, $response->getStatusCode());
$data = json_decode($body);
$data = json_decode($body)->data;
$this->assertEquals('feed', $data->data->type);
$this->assertEquals('feed', $data->type);
} }
public function testShortListOfHEntrysWithHCard() { public function testShortListOfHEntrysWithHCard() {
$url = 'http://feed.example.com/short-list-of-hentrys-with-h-card'; $url = 'http://feed.example.com/short-list-of-hentrys-with-h-card';
$response = $this->parse(['url' => $url]);
$response = $this->parse(['url' => $url, 'expect' => 'feed']);
$body = $response->getContent(); $body = $response->getContent();
$this->assertEquals(200, $response->getStatusCode()); $this->assertEquals(200, $response->getStatusCode());
$data = json_decode($body);
$data = json_decode($body)->data;
$this->assertEquals('entry', $data->data->type);
$this->assertEquals('feed', $data->type);
} }
public function testTopLevelHFeed() { public function testTopLevelHFeed() {
$url = 'http://feed.example.com/top-level-h-feed'; $url = 'http://feed.example.com/top-level-h-feed';
$response = $this->parse(['url' => $url]);
$response = $this->parse(['url' => $url, 'expect' => 'feed']);
$body = $response->getContent(); $body = $response->getContent();
$this->assertEquals(200, $response->getStatusCode()); $this->assertEquals(200, $response->getStatusCode());
$data = json_decode($body);
$data = json_decode($body)->data;
$this->assertEquals('feed', $data->data->type);
$this->assertEquals('feed', $data->type);
} }
public function testHCardWithChildHEntrys() { public function testHCardWithChildHEntrys() {
$url = 'http://feed.example.com/h-card-with-child-h-entrys'; $url = 'http://feed.example.com/h-card-with-child-h-entrys';
$response = $this->parse(['url' => $url]);
$response = $this->parse(['url' => $url, 'expect' => 'feed']);
$body = $response->getContent(); $body = $response->getContent();
$this->assertEquals(200, $response->getStatusCode()); $this->assertEquals(200, $response->getStatusCode());
$data = json_decode($body);
$data = json_decode($body)->data;
$this->assertEquals('card', $data->data->type);
$this->assertEquals('feed', $data->type);
} }
public function testHCardWithChildHFeed() { public function testHCardWithChildHFeed() {
$url = 'http://feed.example.com/h-card-with-child-h-feed'; $url = 'http://feed.example.com/h-card-with-child-h-feed';
$response = $this->parse(['url' => $url]);
$response = $this->parse(['url' => $url, 'expect' => 'feed']);
$body = $response->getContent(); $body = $response->getContent();
$this->assertEquals(200, $response->getStatusCode()); $this->assertEquals(200, $response->getStatusCode());
$data = json_decode($body);
$data = json_decode($body)->data;
$this->assertEquals('card', $data->data->type);
$this->assertEquals('feed', $data->type);
}
public function testAtomFeed() {
$url = 'http://feed.example.com/atom';
$response = $this->parse(['url' => $url, 'expect' => 'feed']);
$body = $response->getContent();
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode($body)->data;
$this->assertEquals(8, count($data->items));
for($i=0; $i<8; $i++) {
$this->assertEquals('entry', $data->items[$i]->type);
$this->assertEquals('Tantek', $data->items[$i]->author->name);
$this->assertEquals('http://tantek.com/', $data->items[$i]->author->url);
$this->assertNotEmpty($data->items[$i]->url);
$this->assertNotEmpty($data->items[$i]->published);
$this->assertNotEmpty($data->items[$i]->content->html);
$this->assertNotEmpty($data->items[$i]->content->text);
}
$this->assertEquals('2017-11-08T23:53:00-08:00', $data->items[0]->published);
$this->assertEquals('http://tantek.com/2017/312/t3/tam-trail-run-first-trail-half', $data->items[0]->url);
$this->assertEquals('went to MORE Pancakes! this morning @RunJanji pop-up on California st after #NPSF. Picked up a new running shirt.', $data->items[1]->content->text);
$this->assertEquals('went to MORE Pancakes! this morning <a href="https://twitter.com/RunJanji">@RunJanji</a> pop-up on California st after #NPSF. Picked up a new running shirt.', $data->items[1]->content->html);
$this->assertEquals('feed', $data->type);
}
public function testRSSFeed() {
$url = 'http://feed.example.com/rss';
$response = $this->parse(['url' => $url, 'expect' => 'feed']);
$body = $response->getContent();
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode($body)->data;
$this->assertEquals(10, count($data->items));
for($i=0; $i<10; $i++) {
$this->assertEquals('entry', $data->items[$i]->type);
$this->assertEquals('Ryan Barrett', $data->items[$i]->author->name);
$this->assertEquals('https://snarfed.org/', $data->items[$i]->author->url);
$this->assertNotEmpty($data->items[$i]->url);
$this->assertNotEmpty($data->items[$i]->published);
$this->assertNotEmpty($data->items[$i]->content->html);
if($i > 1)
$this->assertNotEmpty($data->items[$i]->content->text);
}
$this->assertEquals('2017-09-12T20:09:12+00:00', $data->items[9]->published);
$this->assertEquals('https://snarfed.org/2017-09-12_25492', $data->items[9]->url);
$this->assertEquals('<p>new business cards <img src="https://s.w.org/images/core/emoji/2.3/72x72/1f602.png" alt="😂" /></p>
<p><img src="https://i0.wp.com/snarfed.org/w/wp-content/uploads/2017/09/IMG_20170912_131414_767.jpg?w=696&amp;ssl=1" alt="IMG_20170912_131414_767.jpg?w=696&amp;ssl=1" /></p>', $data->items[9]->content->html);
$this->assertEquals('new business cards', $data->items[9]->content->text);
$this->assertEquals('feed', $data->type);
}
public function testPodcastFeed() {
$url = 'http://feed.example.com/podcast-rss';
$response = $this->parse(['url' => $url, 'expect' => 'feed']);
$body = $response->getContent();
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode($body)->data;
$this->assertEquals(12, count($data->items));
for($i=0; $i<12; $i++) {
$this->assertEquals('entry', $data->items[$i]->type);
$this->assertEquals('Aaron Parecki', $data->items[$i]->author->name);
$this->assertEquals('https://percolator.today/', $data->items[$i]->author->url);
$this->assertNotEmpty($data->items[$i]->url);
$this->assertNotEmpty($data->items[$i]->published);
$this->assertNotEmpty($data->items[$i]->name);
$this->assertNotEmpty($data->items[$i]->content->html);
$this->assertNotEmpty($data->items[$i]->content->text);
$this->assertNotEmpty($data->items[$i]->audio);
}
$this->assertEquals('Episode 1: Welcome', $data->items[11]->name);
$this->assertEquals('https://percolator.today/episode/1', $data->items[11]->url);
$this->assertEquals('2017-09-20T07:00:00+00:00', $data->items[11]->published);
$this->assertEquals('https://percolator.today/redirect.php?url=https%3A%2F%2Fpercolator.today%2Fmedia%2FPercolator_Episode_1.mp3', $data->items[11]->audio);
$this->assertContains('What is Percolator? Some thoughts about multi-photos in Instagram.', $data->items[11]->content->text);
$this->assertContains('What is Percolator? Some thoughts about multi-photos in Instagram.', $data->items[11]->content->html);
$this->assertContains('<li><a href="https://indieweb.org/multi-photo_vs_collection">multi-photo vs collection</a></li>', $data->items[11]->content->html);
$this->assertEquals('feed', $data->type);
} }
} }

+ 201
- 0
tests/data/feed.example.com/atom View File

@ -0,0 +1,201 @@
HTTP/1.1 200 OK
Server: Apache
Date: Wed, 09 Dec 2015 03:29:14 GMT
Content-Type: application/atom+xml; charset=utf-8
Connection: keep-alive
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-US">
<title>Tantek Çelik</title>
<link href="http://tantek.com/" rel="alternate" title="Tantek Çelik" type="text/html"/>
<link href="http://tantek.com/updates.atom" rel="self"/>
<link rel="hub" href="https://pubsubhubbub.superfeedr.com/"/>
<id>http://tantek.com/updates.atom</id>
<author>
<name>Tantek</name>
<uri>http://tantek.com/</uri>
</author>
<updated>2017-11-08T23:53:00-08:00</updated>
<entry>
<updated>2017-11-08T23:53:00-08:00</updated>
<published>2017-11-08T23:53:00-08:00</published>
<link href="http://tantek.com/2017/312/t3/tam-trail-run-first-trail-half" rel="alternate" title="going to Mt. Tam Trail Run 2017-11-11 07:00 at Stinson Beach! signed up for the half (my first trail half marathon!)" type="text/html"/>
<id>http://tantek.com/2017/312/t3/tam-trail-run-first-trail-half</id>
<title type="xhtml">
<div xmlns="http://www.w3.org/1999/xhtml" class="if-your-feed-reader-displays-this-then-it-is-violating-the-Atom-spec-RFC-4287-section-4.2.14">going to Mt. Tam Trail Run 2017-11-11 07:00 at Stinson Beach!
signed up for the half (my first trail half marathon!)</div>
</title>
<content type="xhtml" xml:base="http://tantek.com/2017/" xml:space="preserve">
<div xmlns="http://www.w3.org/1999/xhtml" xml:space="preserve">going to Mt. Tam Trail Run 2017-11-11 07:00 at Stinson Beach!<br class="auto-break"/>signed up for the half (my first trail half marathon!)</div>
</content>
<object-type xmlns="http://activitystrea.ms/spec/1.0/">note</object-type>
</entry>
<entry>
<updated>2017-11-08T23:29:00-08:00</updated>
<published>2017-11-08T23:29:00-08:00</published>
<link href="http://tantek.com/2017/312/t2/more-pancakes-this-morning" rel="alternate" title="went to MORE Pancakes! this morning @RunJanji pop-up on California st after #NPSF. Picked up a new running shirt." type="text/html"/>
<id>http://tantek.com/2017/312/t2/more-pancakes-this-morning</id>
<title type="xhtml">
<div xmlns="http://www.w3.org/1999/xhtml" class="if-your-feed-reader-displays-this-then-it-is-violating-the-Atom-spec-RFC-4287-section-4.2.14">went to MORE Pancakes! this morning @RunJanji pop-up on California st after #NPSF. Picked up a new running shirt.</div>
</title>
<content type="xhtml" xml:base="http://tantek.com/2017/" xml:space="preserve">
<div xmlns="http://www.w3.org/1999/xhtml" xml:space="preserve">went to MORE Pancakes! this morning <a class="auto-link h-cassis-username" href="https://twitter.com/RunJanji">@RunJanji</a> pop-up on California st after #<span class="p-category auto-tag">NPSF</span>. Picked up a new running shirt.</div>
</content>
<object-type xmlns="http://activitystrea.ms/spec/1.0/">note</object-type>
</entry>
<entry>
<updated>2017-11-08T13:19:00-08:00</updated>
<published>2017-11-08T13:19:00-08:00</published>
<link href="http://tantek.com/2017/312/t1/w3ctpac-csswg-new-draft-barcamp" rel="alternate" title="at #w3cTPAC all week: MT @CSSWG; I made a new draft W Intro’d &amp; facilitated @W3C Plenary day #BarCamp ThF #WPWG #WICG" type="text/html"/>
<id>http://tantek.com/2017/312/t1/w3ctpac-csswg-new-draft-barcamp</id>
<title type="xhtml">
<div xmlns="http://www.w3.org/1999/xhtml" class="if-your-feed-reader-displays-this-then-it-is-violating-the-Atom-spec-RFC-4287-section-4.2.14">at #w3cTPAC all week:
MT @CSSWG; I made a new draft
W Intro’d &amp; facilitated @W3C Plenary day #BarCamp
ThF #WPWG #WICG</div>
</title>
<content type="xhtml" xml:base="http://tantek.com/2017/" xml:space="preserve">
<div xmlns="http://www.w3.org/1999/xhtml" xml:space="preserve">at #<span class="p-category auto-tag">w3cTPAC</span> all week:<br class="auto-break"/>MT <a class="auto-link h-cassis-username" href="https://twitter.com/CSSWG">@CSSWG</a>; I made a new draft<br class="auto-break"/>W Intro’d &amp; facilitated <a class="auto-link h-cassis-username" href="https://twitter.com/W3C">@W3C</a> Plenary day #<span class="p-category auto-tag">BarCamp</span><br class="auto-break"/>ThF #<span class="p-category auto-tag">WPWG</span> #<span class="p-category auto-tag">WICG</span></div>
</content>
<object-type xmlns="http://activitystrea.ms/spec/1.0/">note</object-type>
</entry>
<entry>
<updated>2017-10-31T20:16:00-07:00</updated>
<published>2017-10-31T20:16:00-07:00</published>
<link href="http://tantek.com/2017/304/t1/run-bay-to-breakers-2018" rel="alternate" title="going to run Bay to Breakers 2018-05-20 + Bonus 3k = 15k! join me in Corral A: https://baytobreakers.com/ $40 today only!" type="text/html"/>
<id>http://tantek.com/2017/304/t1/run-bay-to-breakers-2018</id>
<title type="xhtml">
<div xmlns="http://www.w3.org/1999/xhtml" class="if-your-feed-reader-displays-this-then-it-is-violating-the-Atom-spec-RFC-4287-section-4.2.14">going to run Bay to Breakers 2018-05-20 + Bonus 3k = 15k!
join me in Corral A: https://baytobreakers.com/
$40 today only!</div>
</title>
<content type="xhtml" xml:base="http://tantek.com/2017/" xml:space="preserve">
<div xmlns="http://www.w3.org/1999/xhtml" xml:space="preserve">going to run Bay to Breakers 2018-05-20 + Bonus 3k = 15k!<br class="auto-break"/>join me in Corral A: <a class="auto-link" href="https://baytobreakers.com/">https://baytobreakers.com/</a><br class="auto-break"/>$40 today only!</div>
</content>
<object-type xmlns="http://activitystrea.ms/spec/1.0/">note</object-type>
</entry>
<entry>
<updated>2017-10-30T21:20:00-07:00</updated>
<published>2017-10-30T21:20:00-07:00</published>
<link href="http://tantek.com/2017/303/t3/amp-openwashing-like-schema" rel="alternate" title=".@beep https://ethanmarcotte.com/wrote/seven-into-seven/ &amp; @adactio https://adactio.com/journal/13035 I.e. #AMP is #openwashing not #openweb, similar to #schema before it. It has appearances or aspects of being open (e.g. with defensive statements like &quot;non-google committers&quot;), but in practice is proprietarily controlled/governed (free community labor for corporate leaders), and designed (perhaps unintentionally) to bias an otherwise seemingly open ecosystem towards a single company or small anti-competitive oligopoly in practice. Nevermind the #doublespeak of caching vs hosting, or placement within &quot;search results&quot; implying part of a page vs the more practically meaningful &quot;SERP&quot; (search engine results *page*) placement, regardless of styling as a horizontal carousel or vertical list. I still think an actually open alternative could be beneficial. Previously, previously, previously: * tantek.com/2017/261/t2/open-alternatives-googleamp * tantek.com/2017/262/t1/amp-yourself-worse-than-html * tantek.com/2017/262/t2/amp-worse-than-html-subset * tantek.com/2017/263/t1/open-html-subset-interacts-quickly * tantek.com/2015/280/t1/js-dr-amp-not-include-javascript * tantek.com/2015/280/t2/make-amp-smaller-ban-jsonld-schema-org More: https://indieweb.org/AMP" type="text/html"/>
<id>http://tantek.com/2017/303/t3/amp-openwashing-like-schema</id>
<title type="xhtml">
<div xmlns="http://www.w3.org/1999/xhtml" class="if-your-feed-reader-displays-this-then-it-is-violating-the-Atom-spec-RFC-4287-section-4.2.14">.@beep https://ethanmarcotte.com/wrote/seven-into-seven/
&amp; @adactio https://adactio.com/journal/13035
I.e. #AMP is #openwashing not #openweb, similar to #schema before it.
It has appearances or aspects of being open (e.g. with defensive statements like "non-google committers"), but in practice is proprietarily controlled/governed (free community labor for corporate leaders), and designed (perhaps unintentionally) to bias an otherwise seemingly open ecosystem towards a single company or small anti-competitive oligopoly in practice.
Nevermind the #doublespeak of caching vs hosting, or placement within "search results" implying part of a page vs the more practically meaningful "SERP" (search engine results *page*) placement, regardless of styling as a horizontal carousel or vertical list.
I still think an actually open alternative could be beneficial.
Previously, previously, previously:
* tantek.com/2017/261/t2/open-alternatives-googleamp
* tantek.com/2017/262/t1/amp-yourself-worse-than-html
* tantek.com/2017/262/t2/amp-worse-than-html-subset
* tantek.com/2017/263/t1/open-html-subset-interacts-quickly
* tantek.com/2015/280/t1/js-dr-amp-not-include-javascript
* tantek.com/2015/280/t2/make-amp-smaller-ban-jsonld-schema-org
More: https://indieweb.org/AMP</div>
</title>
<content type="xhtml" xml:base="http://tantek.com/2017/" xml:space="preserve">
<div xmlns="http://www.w3.org/1999/xhtml" xml:space="preserve">.<a class="auto-link h-cassis-username" href="https://twitter.com/beep">@beep</a> <a class="auto-link" href="https://ethanmarcotte.com/wrote/seven-into-seven/">https://ethanmarcotte.com/wrote/seven-into-seven/</a><br class="auto-break"/>&amp; <a class="auto-link h-cassis-username" href="https://twitter.com/adactio">@adactio</a> <a class="auto-link" href="https://adactio.com/journal/13035">https://adactio.com/journal/13035</a><br class="auto-break"/>I.e. #<span class="p-category auto-tag">AMP</span> is #<span class="p-category auto-tag">openwashing</span> not #<span class="p-category auto-tag">openweb</span>, similar to #<span class="p-category auto-tag">schema</span> before it.<br class="auto-break"/><br class="auto-break"/>It has appearances or aspects of being open (e.g. with defensive statements like "non-google committers"), but in practice is proprietarily controlled/governed (free community labor for corporate leaders), and designed (perhaps unintentionally) to bias an otherwise seemingly open ecosystem towards a single company or small anti-competitive oligopoly in practice.<br class="auto-break"/><br class="auto-break"/>Nevermind the #<span class="p-category auto-tag">doublespeak</span> of caching vs hosting, or placement within "search results" implying part of a page vs the more practically meaningful "SERP" (search engine results *page*) placement, regardless of styling as a horizontal carousel or vertical list.<br class="auto-break"/><br class="auto-break"/>I still think an actually open alternative could be beneficial.<br class="auto-break"/><br class="auto-break"/>Previously, previously, previously:<br class="auto-break"/>* <a class="auto-link" href="http://tantek.com/2017/261/t2/open-alternatives-googleamp">tantek.com/2017/261/t2/open-alternatives-googleamp</a><br class="auto-break"/> * <a class="auto-link" href="http://tantek.com/2017/262/t1/amp-yourself-worse-than-html">tantek.com/2017/262/t1/amp-yourself-worse-than-html</a><br class="auto-break"/> * <a class="auto-link" href="http://tantek.com/2017/262/t2/amp-worse-than-html-subset">tantek.com/2017/262/t2/amp-worse-than-html-subset</a><br class="auto-break"/> * <a class="auto-link" href="http://tantek.com/2017/263/t1/open-html-subset-interacts-quickly">tantek.com/2017/263/t1/open-html-subset-interacts-quickly</a><br class="auto-break"/>* <a class="auto-link" href="http://tantek.com/2015/280/t1/js-dr-amp-not-include-javascript">tantek.com/2015/280/t1/js-dr-amp-not-include-javascript</a><br class="auto-break"/> * <a class="auto-link" href="http://tantek.com/2015/280/t2/make-amp-smaller-ban-jsonld-schema-org">tantek.com/2015/280/t2/make-amp-smaller-ban-jsonld-schema-org</a><br class="auto-break"/><br class="auto-break"/>More: <a class="auto-link" href="https://indieweb.org/AMP">https://indieweb.org/AMP</a></div>
</content>
<object-type xmlns="http://activitystrea.ms/spec/1.0/">note</object-type>
</entry>
<entry>
<updated>2017-10-30T16:57:00-07:00</updated>
<published>2017-10-30T16:57:00-07:00</published>
<link href="http://tantek.com/2017/303/t2/frontend-web-dev-designer-fullstack-follow-aeasf" rel="alternate" title="If you’re a frontend web dev designer or fullstack-*, you must follow #AEASF for pure awesome: https://twitter.com/hashtag/aeasf?f=tweets&amp;vertical=default" type="text/html"/>
<id>http://tantek.com/2017/303/t2/frontend-web-dev-designer-fullstack-follow-aeasf</id>
<title type="xhtml">
<div xmlns="http://www.w3.org/1999/xhtml" class="if-your-feed-reader-displays-this-then-it-is-violating-the-Atom-spec-RFC-4287-section-4.2.14">If you’re a frontend web dev designer or fullstack-*, you must follow #AEASF for pure awesome: https://twitter.com/hashtag/aeasf?f=tweets&amp;vertical=default</div>
</title>
<content type="xhtml" xml:base="http://tantek.com/2017/" xml:space="preserve">
<div xmlns="http://www.w3.org/1999/xhtml" xml:space="preserve">If you’re a frontend web dev designer or fullstack-*, you must follow #<span class="p-category auto-tag">AEASF</span> for pure awesome: <a class="auto-link" href="https://twitter.com/hashtag/aeasf?f=tweets&amp;vertical=default">https://twitter.com/hashtag/aeasf?f=tweets&amp;vertical=default</a></div>
</content>
<object-type xmlns="http://activitystrea.ms/spec/1.0/">note</object-type>
</entry>
<entry>
<updated>2017-10-30T08:30:00-07:00</updated>
<published>2017-10-30T08:30:00-07:00</published>
<link href="http://tantek.com/2017/303/t1/four-years-nov-project-sf-npversary" rel="alternate" title="Four years ago: my first @Nov_Project_SF. #NPversary This past year: ran my first marathon &amp; led #NP_Yoga #NovemberProject is an amazing community. Grateful to be a part of it, across so many cities, and especially grateful for all the support which helped me breakthrough limitations I had previously accepted, and achieve goals I had thought were impossible or had never even considered. Let’s see what I can do in year 5. Previously: * tantek.com/2014/308/b2/my-first-year-november-project * tantek.com/2014/303/t1/one-year-ago-justshowup-npsf-npversary * tantek.com/2013/303/t3/novemberproject-different-level-fitness" type="text/html"/>
<id>http://tantek.com/2017/303/t1/four-years-nov-project-sf-npversary</id>
<title type="xhtml">
<div xmlns="http://www.w3.org/1999/xhtml" class="if-your-feed-reader-displays-this-then-it-is-violating-the-Atom-spec-RFC-4287-section-4.2.14">Four years ago: my first @Nov_Project_SF. #NPversary
This past year: ran my first marathon &amp; led #NP_Yoga
#NovemberProject is an amazing community.
Grateful to be a part of it, across so many cities, and especially grateful for all the support which helped me breakthrough limitations I had previously accepted, and achieve goals I had thought were impossible or had never even considered.
Let’s see what I can do in year 5.
Previously:
* tantek.com/2014/308/b2/my-first-year-november-project
* tantek.com/2014/303/t1/one-year-ago-justshowup-npsf-npversary
* tantek.com/2013/303/t3/novemberproject-different-level-fitness</div>
</title>
<content type="xhtml" xml:base="http://tantek.com/2017/" xml:space="preserve">
<div xmlns="http://www.w3.org/1999/xhtml" xml:space="preserve">Four years ago: my first <a class="auto-link h-cassis-username" href="https://twitter.com/Nov_Project_SF">@Nov_Project_SF</a>. #<span class="p-category auto-tag">NPversary</span><br class="auto-break"/>This past year: ran my first marathon &amp; led #<span class="p-category auto-tag">NP_Yoga</span> <br class="auto-break"/><br class="auto-break"/>#<span class="p-category auto-tag">NovemberProject</span> is an amazing community.<br class="auto-break"/><br class="auto-break"/>Grateful to be a part of it, across so many cities, and especially grateful for all the support which helped me breakthrough limitations I had previously accepted, and achieve goals I had thought were impossible or had never even considered.<br class="auto-break"/><br class="auto-break"/>Let’s see what I can do in year 5.<br class="auto-break"/><br class="auto-break"/>Previously:<br class="auto-break"/>* <a class="auto-link" href="http://tantek.com/2014/308/b2/my-first-year-november-project">tantek.com/2014/308/b2/my-first-year-november-project</a><br class="auto-break"/>* <a class="auto-link" href="http://tantek.com/2014/303/t1/one-year-ago-justshowup-npsf-npversary">tantek.com/2014/303/t1/one-year-ago-justshowup-npsf-npversary</a><br class="auto-break"/>* <a class="auto-link" href="http://tantek.com/2013/303/t3/novemberproject-different-level-fitness">tantek.com/2013/303/t3/novemberproject-different-level-fitness</a></div>
</content>
<object-type xmlns="http://activitystrea.ms/spec/1.0/">note</object-type>
</entry>
<entry>
<updated>2017-10-29T16:26:00-07:00</updated>
<published>2017-10-29T16:26:00-07:00</published>
<link href="http://tantek.com/2017/305/e1/homebrew-website-club" rel="alternate" title="Homebrew Website Club SF" type="text/html"/>
<id>http://tantek.com/2017/305/e1/homebrew-website-club</id>
<title type="xhtml">
<div xmlns="http://www.w3.org/1999/xhtml" class="if-your-feed-reader-displays-this-then-it-is-violating-the-Atom-spec-RFC-4287-section-4.2.14">Event</div>
</title>
<content type="xhtml" xml:base="http://tantek.com/2017/" xml:space="preserve">
<div xmlns="http://www.w3.org/1999/xhtml" xml:space="preserve">
<div class="entry-content">
<p>
When: <time class="dt-start dtstart">2017-11-01 17:30</time>…<time class="dt-end dtend">19:30</time>
<span style="display:block">
Where: <a class="u-location h-card location" href="https://wiki.mozilla.org/SF">Mozilla San Francisco</a>
</span>
Host: <a class="u-organizer h-card" href="http://tantek.com/">Tantek Çelik</a>
</p>
<div class="p-summary description">
<p>
17:30: Optional writing hour and quiet socializing<br/>
18:30: IndieWeb demos and hack night
</p>
<p><img class="u-featured" style="max-width:100%;width:100%;height:300px;object-fit:cover;object-position:50% 40%" src="https://indieweb.org/images/b/b1/2017-hwc-80s-retro.jpg" alt="Photo of previous Homebrew Website Club participants"/></p>
<p>
Demos of personal website breakthroughs. Create or update your personal web site!
</p>
<p>
Join a community with like-minded interests. Bring friends that want a personal site, or are interested in a healthy, independent web!
</p>
<p>Topics for this week:</p>
<ul>
<li><a href="https://indieweb.org/2017/Berlin">IndieWebCamp Berlin</a> coming up this weekend!</li>
<li><a href="https://indieweb.org/post#Thoughts_on_why_post">Why post (or not)</a></li>
<li><a href="https://indieweb.org/WebSub">WebSub</a> (IndieWeb building block) is a <a href="https://www.w3.org/blog/news/archives/6555">W3C Proposed Recommendation</a></li>
<li><a href="https://www.w3.org/wiki/TPAC/2017">W3C TPAC is next week</a> in Burlingame (near SF)</li>
<li><a href="https://indieweb.org/2018-01-01-commitments">2018-01-01-commitments</a></li>
</ul>
<p>
Any questions? Ask in
<a href="https://indieweb.org/discuss">#indieweb Slack or IRC</a>
</p>
<p>
More information:
<a class="u-url" href="https://indieweb.org/events/2017-11-01-homebrew-website-club">IndieWeb Wiki Event Page</a>
</p>
<p>
RSVP: <a class="u-syndication" href="https://www.facebook.com/events/475348969517825">on the Facebook event</a> or post an <a href="https://indieweb.org/rsvp">indie RSVP</a> on your own site!
</p>
</div>
</div>
</div>
</content>
<object-type xmlns="http://activitystrea.ms/spec/1.0/">event</object-type>
</entry>
</feed>

+ 297
- 0
tests/data/feed.example.com/podcast-rss View File

@ -0,0 +1,297 @@
HTTP/1.1 200 OK
Server: Apache
Date: Wed, 09 Dec 2015 03:29:14 GMT
Content-Type: application/rss+xml; charset=utf-8
Connection: keep-alive
<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="https://percolator.today/stylesheet.xsl" ?>
<rss version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
>
<channel>
<title>Percolator</title>
<atom:link href="https://percolator.today/podcast.xml" rel="self" type="application/rss+xml" />
<atom:link href="https://switchboard.p3k.io/" rel="hub" />
<link>https://percolator.today/</link>
<description>A microcast with Aaron Parecki</description>
<lastBuildDate>Wed, 20 Sep 2017 07:00:00 +0000</lastBuildDate>
<language>en-US</language>
<copyright>2017 Aaron Parecki</copyright>
<itunes:subtitle></itunes:subtitle>
<itunes:author>Aaron Parecki</itunes:author>
<itunes:summary>A microcast with Aaron Parecki</itunes:summary>
<itunes:owner>
<itunes:name>Aaron Parecki</itunes:name>
<itunes:email>aaron@parecki.com</itunes:email>
</itunes:owner>
<itunes:explicit>No</itunes:explicit>
<itunes:image href="https://percolator.today/images/cover.jpg"></itunes:image>
<itunes:category text="Society &amp; Culture">
<itunes:category text="Personal Journals"></itunes:category>
</itunes:category>
<item>
<title>Episode 12: To Poll or Not to Poll</title>
<link>https://percolator.today/episode/12</link>
<pubDate>Fri, 10 Nov 2017 00:00:00 +0000</pubDate>
<dc:creator>Aaron Parecki</dc:creator>
<guid isPermaLink="true">https://percolator.today/episode/12</guid>
<description><![CDATA[<p>Building out a feed polling infrastructure is daunting. Thankfully Superfeedr has done it for me.</p>
<ul>
<li><a href="http://superfeedr.com/">Superfeedr</a></li>
<li><a href="https://indieweb.org/Microsub">Microsub</a></li>
<li><a href="https://monocle.p3k.io/">Monocle</a></li>
</ul>
]]></description>
<itunes:summary><![CDATA[Building out a feed polling infrastructure is daunting.]]></itunes:summary>
<enclosure url="https://percolator.today/redirect.php?url=https%3A%2F%2Fpercolator.today%2Fmedia%2FEpisode_12.mp3" length="3734380" type="audio/mpeg"></enclosure>
<itunes:explicit>No</itunes:explicit>
<itunes:block>No</itunes:block>
<itunes:duration>03:07</itunes:duration>
<itunes:author>Aaron Parecki</itunes:author>
</item>
<item>
<title>Episode 11: What are Instagram Stories?</title>
<link>https://percolator.today/episode/11</link>
<pubDate>Sat, 04 Nov 2017 00:00:00 +0000</pubDate>
<dc:creator>Aaron Parecki</dc:creator>
<guid isPermaLink="true">https://percolator.today/episode/11</guid>
<description><![CDATA[<p>A bit of a diversion from my normal routine where I implemented Instagram Stories on my own website.</p>
<ul>
<li><a href="https://aaronparecki.com">aaronparecki.com</a></li>
<li><a href="https://indieweb.org/story">indieweb.org/story</a></li>
<li><a href="https://streampdx.com">streampdx.com</a></li>
</ul>
]]></description>
<itunes:summary><![CDATA[A bit of a diversion from my normal routine where I implemented Instagram Stories on my own website.]]></itunes:summary>
<enclosure url="https://percolator.today/redirect.php?url=https%3A%2F%2Fpercolator.today%2Fmedia%2FEpisode_11.mp3" length="3642429" type="audio/mpeg"></enclosure>
<itunes:explicit>No</itunes:explicit>
<itunes:block>No</itunes:block>
<itunes:duration>03:01</itunes:duration>
<itunes:author>Aaron Parecki</itunes:author>
</item>
<item>
<title>Episode 10: Microsub for Readers</title>
<link>https://percolator.today/episode/10</link>
<pubDate>Fri, 03 Nov 2017 00:00:00 +0000</pubDate>
<dc:creator>Aaron Parecki</dc:creator>
<guid isPermaLink="true">https://percolator.today/episode/10</guid>
<description><![CDATA[<p>Itching to get started rebuilding Monocle.</p>
<ul>
<li><a href="https://indieweb.org/Microsub-spec">Microsub draft spec</a></li>
<li><a href="https://aaronparecki.com/2016/04/26/3/monocle">Monocle is Offline</a></li>
</ul>
]]></description>
<itunes:summary><![CDATA[Itching to get started rebuilding Monocle]]></itunes:summary>
<enclosure url="https://percolator.today/redirect.php?url=https%3A%2F%2Fpercolator.today%2Fmedia%2FEpisode_10.mp3" length="2414883" type="audio/mpeg"></enclosure>
<itunes:explicit>No</itunes:explicit>
<itunes:block>No</itunes:block>
<itunes:duration>01:44</itunes:duration>
<itunes:author>Aaron Parecki</itunes:author>
</item>
<item>
<title>Episode 9: Streamlining my Microcast Workflow</title>
<link>https://percolator.today/episode/9</link>
<pubDate>Mon, 30 Oct 2017 00:00:00 +0000</pubDate>
<dc:creator>Aaron Parecki</dc:creator>
<guid isPermaLink="true">https://percolator.today/episode/9</guid>
<description><![CDATA[<p>I wrote a new Micropub app to help me publish Microcast episodes quicker. You can use it too! My podcast website also receives webmentions now!</p>
<ul>
<li><a href="https://gramophone.p3k.io">Gramophone</a></li>
<li><a href="https://micropub.net/">Micropub</a></li>
<li><a href="https://webmention.net/">Webmention</a></li>
</ul>
]]></description>
<itunes:summary><![CDATA[I wrote a new Micropub app to help me publish Microcast episodes quicker]]></itunes:summary>
<enclosure url="https://percolator.today/redirect.php?url=https%3A%2F%2Fpercolator.today%2Fmedia%2FEpisode_9.mp3" length="3351530" type="audio/mpeg"></enclosure>
<itunes:explicit>No</itunes:explicit>
<itunes:block>No</itunes:block>
<itunes:duration>02:43</itunes:duration>
<itunes:author>Aaron Parecki</itunes:author>
</item>
<item>
<title>Episode 8: Back from the Woods</title>
<link>https://percolator.today/episode/8</link>
<pubDate>Sat, 28 Oct 2017 07:00:00 +0000</pubDate>
<dc:creator>Aaron Parecki</dc:creator>
<guid isPermaLink="true">https://percolator.today/episode/8</guid>
<description><![CDATA[<p>Reflections on how I control what I consume online.</p>
<p>Links:</p>
<ul>
<li><a href="https://monocle.p3k.io/">Monocle</a></li>
<li><a href="https://aaronparecki.com/2015/08/29/8/why-i-live-in-irc">Why I Live in IRC</a></li>
</ul>
]]></description>
<itunes:summary><![CDATA[Reflections on how I control what I consume online]]></itunes:summary>
<enclosure url="https://percolator.today/redirect.php?url=https%3A%2F%2Fpercolator.today%2Fmedia%2FEpisode_8.mp3" length="4036147" type="audio/mpeg"></enclosure>
<itunes:explicit>No</itunes:explicit>
<itunes:block>No</itunes:block>
<itunes:duration>03:25</itunes:duration>
<itunes:author>Aaron Parecki</itunes:author>
</item>
<item>
<title>Episode 7: Making Micropub More Useful</title>
<link>https://percolator.today/episode/7</link>
<pubDate>Thu, 12 Oct 2017 07:00:00 +0000</pubDate>
<dc:creator>Aaron Parecki</dc:creator>
<guid isPermaLink="true">https://percolator.today/episode/7</guid>
<description><![CDATA[<p>Thinking about what is needed for the Micropub spec for it to be even more useful.</p>
<p>Links</p>
<ul>
<li><a href="https://quill.p3k.io">Quill</a> - a web-based Micropub app</li>
<li><a href="https://micro.blog">Micro.blog</a></li>
<li><a href="https://omnibear.com/">Omnibear</a> - a Micropub browser extension</li>
<li><a href="https://indieweb.org/POSSE">POSSE</a> - Publish On your Own Site, Syndicate Elsewhere</li>
<li><a href="https://indieauth.net">IndieAuth</a></li>
<li><a href="https://micropub.net">Micropub</a></li>
</ul>
]]></description>
<itunes:summary><![CDATA[Thinking about what is needed for the Micropub spec for it to be even more useful.]]></itunes:summary>
<enclosure url="https://percolator.today/redirect.php?url=https%3A%2F%2Fpercolator.today%2Fmedia%2FPercolator_Episode_7.mp3" length="4719178" type="audio/mpeg"></enclosure>
<itunes:explicit>No</itunes:explicit>
<itunes:block>No</itunes:block>
<itunes:duration>05:31</itunes:duration>
<itunes:author>Aaron Parecki</itunes:author>
</item>
<item>
<title>Episode 6: My New Superpower</title>
<link>https://percolator.today/episode/6</link>
<pubDate>Mon, 09 Oct 2017 07:00:00 +0000</pubDate>
<dc:creator>Aaron Parecki</dc:creator>
<guid isPermaLink="true">https://percolator.today/episode/6</guid>
<description><![CDATA[<p>My Overland GPS tracker app is live in the app store!</p>
<p>Links:</p>
<ul>
<li><a href="https://overland.p3k.io/">overland.p3k.io</a></li>
<li><a href="https://github.com/aaronpk/Overland-iOS">source code</a></li>
</ul>
]]></description>
<itunes:summary><![CDATA[My Overland GPS tracker app is live in the app store!]]></itunes:summary>
<enclosure url="https://percolator.today/redirect.php?url=https%3A%2F%2Fpercolator.today%2Fmedia%2FPercolator_Episode_6.mp3" length="3214274" type="audio/mpeg"></enclosure>
<itunes:explicit>No</itunes:explicit>
<itunes:block>No</itunes:block>
<itunes:duration>03:26</itunes:duration>
<itunes:author>Aaron Parecki</itunes:author>
</item>
<item>
<title>Episode 5: Homebrew Website Club</title>
<link>https://percolator.today/episode/5</link>
<pubDate>Fri, 06 Oct 2017 07:00:00 +0000</pubDate>
<dc:creator>Aaron Parecki</dc:creator>
<guid isPermaLink="true">https://percolator.today/episode/5</guid>
<description><![CDATA[<p>We had a great Portland Homebrew Website Club!</p>
<p>Links:</p>
<ul>
<li><a href="https://indieweb.org/events/2017-10-04-homebrew-website-club">Homebrew Website Club October 4th</a></li>
<li><a href="https://aaronparecki.com/2017/10/04/23/passwordless-logins">Passwordless Logins on my Website</a></li>
</ul>
]]></description>
<itunes:summary><![CDATA[We had a great Portland Homebrew Website Club!]]></itunes:summary>
<enclosure url="https://percolator.today/redirect.php?url=https%3A%2F%2Fpercolator.today%2Fmedia%2FPercolator_Episode_5.mp3" length="2303424" type="audio/mpeg"></enclosure>
<itunes:explicit>No</itunes:explicit>
<itunes:block>No</itunes:block>
<itunes:duration>02:10</itunes:duration>
<itunes:author>Aaron Parecki</itunes:author>
</item>
<item>
<title>Episode 4: Pretending to be an iOS Developer</title>
<link>https://percolator.today/episode/4</link>
<pubDate>Wed, 04 Oct 2017 07:00:00 +0000</pubDate>
<dc:creator>Aaron Parecki</dc:creator>
<guid isPermaLink="true">https://percolator.today/episode/4</guid>
<description><![CDATA[<p>Talking about what it takes to publish an app on the iOS app store.</p>
<p>Links:</p>
<ul>
<li><a href="https://aaronparecki.com/gps/">about my GPS logs</a></li>
<li><a href="https://overland.p3k.io">Overland website</a></li>
<li><a href="https://github.com/aaronpk/Overland-iOS">Overland on GitHub</a></li>
</ul>
]]></description>
<itunes:summary><![CDATA[Talking about what it takes to publish an app on the iOS app store.]]></itunes:summary>
<enclosure url="https://percolator.today/redirect.php?url=https%3A%2F%2Fpercolator.today%2Fmedia%2FPercolator_Episode_4.mp3" length="4631320" type="audio/mpeg"></enclosure>
<itunes:explicit>No</itunes:explicit>
<itunes:block>No</itunes:block>
<itunes:duration>05:24</itunes:duration>
<itunes:author>Aaron Parecki</itunes:author>
</item>
<item>
<title>Episode 3: Following</title>
<link>https://percolator.today/episode/3</link>
<pubDate>Fri, 29 Sep 2017 07:00:00 +0000</pubDate>
<dc:creator>Aaron Parecki</dc:creator>
<guid isPermaLink="true">https://percolator.today/episode/3</guid>
<description><![CDATA[<p>Thinking about how I want to follow people on the IndieWeb.</p>
<p>Links:</p>
<ul>
<li><a href="https://indieweb.org/follow">follow</a></li>
<li><a href="https://indieweb.org/webactions#Issues">issues with webactions</a></li>
<li><a href="https://keithjgrant.com/">Keith Grant</a></li>
<li><a href="https://omnibear.com/">Omnibear browser extension</a></li>
</ul>
]]></description>
<itunes:summary><![CDATA[Thinking about how I want to follow people on the IndieWeb.]]></itunes:summary>
<enclosure url="https://percolator.today/redirect.php?url=https%3A%2F%2Fpercolator.today%2Fmedia%2FPercolator_Episode_3.mp3" length="4343275" type="audio/mpeg"></enclosure>
<itunes:explicit>No</itunes:explicit>
<itunes:block>No</itunes:block>
<itunes:duration>05:00</itunes:duration>
<itunes:author>Aaron Parecki</itunes:author>
</item>
<item>
<title>Episode 2: Publishing a Podcast</title>
<link>https://percolator.today/episode/2</link>
<pubDate>Fri, 22 Sep 2017 07:00:00 +0000</pubDate>
<dc:creator>Aaron Parecki</dc:creator>
<guid isPermaLink="true">https://percolator.today/episode/2</guid>
<description><![CDATA[<p>Building a website for this podcast, enjoying a new coffee shop.</p>
<p><a href="https://aaronparecki.com/2017/09/21/5/">Foxy coffee</a></p>
]]></description>
<itunes:summary><![CDATA[Building a website for this podcast, enjoying a new coffee shop.]]></itunes:summary>
<enclosure url="https://percolator.today/redirect.php?url=https%3A%2F%2Fpercolator.today%2Fmedia%2FPercolator_Episode_2.mp3" length="3657711" type="audio/mpeg"></enclosure>
<itunes:explicit>No</itunes:explicit>
<itunes:block>No</itunes:block>
<itunes:duration>04:02</itunes:duration>
<itunes:author>Aaron Parecki</itunes:author>
</item>
<item>
<title>Episode 1: Welcome</title>
<link>https://percolator.today/episode/1</link>
<pubDate>Wed, 20 Sep 2017 07:00:00 +0000</pubDate>
<dc:creator>Aaron Parecki</dc:creator>
<guid isPermaLink="true">https://percolator.today/episode/1</guid>
<description><![CDATA[<p>What is Percolator? Some thoughts about multi-photos in Instagram.</p>
<p>Links:</p>
<ul>
<li><a href="https://indieweb.org/multi-photo_vs_collection">multi-photo vs collection</a></li>
</ul>
]]></description>
<itunes:summary><![CDATA[What is Percolator? Some thoughts about multi-photos in Instagram.]]></itunes:summary>
<enclosure url="https://percolator.today/redirect.php?url=https%3A%2F%2Fpercolator.today%2Fmedia%2FPercolator_Episode_1.mp3" length="3090320" type="audio/mpeg"></enclosure>
<itunes:explicit>No</itunes:explicit>
<itunes:block>No</itunes:block>
<itunes:duration>03:15</itunes:duration>
<itunes:author>Aaron Parecki</itunes:author>
</item>
</channel>
</rss>

+ 197
- 0
tests/data/feed.example.com/rss View File

@ -0,0 +1,197 @@
HTTP/1.1 200 OK
Server: Apache
Date: Wed, 09 Dec 2015 03:29:14 GMT
Content-Type: application/rss+xml; charset=utf-8
Connection: keep-alive
<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
>
<channel>
<title>snarfed.org</title>
<atom:link href="https://snarfed.org/feed" rel="self" type="application/rss+xml" />
<link>https://snarfed.org</link>
<description>Ryan Barrett&#039;s blog</description>
<lastBuildDate>Sat, 11 Nov 2017 00:30:06 +0000</lastBuildDate>
<language>en-US</language>
<sy:updatePeriod>hourly</sy:updatePeriod>
<sy:updateFrequency>1</sy:updateFrequency>
<generator>https://wordpress.org/?v=4.8.3</generator>
<item>
<title></title>
<link>https://snarfed.org/2017-11-05_25889</link>
<comments>https://snarfed.org/2017-11-05_25889#comments</comments>
<pubDate>Mon, 06 Nov 2017 02:15:02 +0000</pubDate>
<dc:creator><![CDATA[Ryan Barrett]]></dc:creator>
<category><![CDATA[Uncategorized]]></category>
<guid isPermaLink="false">https://snarfed.org/?p=25889</guid>
<description><![CDATA[]]></description>
<content:encoded><![CDATA[<p><a class="shadow" href="https://i2.wp.com/snarfed.org/w/wp-content/uploads/2017/11/IMG_20171105_173400_955.jpg?ssl=1"><img class="size-full shadow" src="https://i2.wp.com/snarfed.org/w/wp-content/uploads/2017/11/IMG_20171105_173400_955.jpg?resize=696%2C696&#038;ssl=1" data-recalc-dims="1"></a></p>
]]></content:encoded>
<wfw:commentRss>https://snarfed.org/2017-11-05_25889/feed</wfw:commentRss>
<slash:comments>12</slash:comments>
</item>
<item>
<title></title>
<link>https://snarfed.org/2017-10-31_25851</link>
<comments>https://snarfed.org/2017-10-31_25851#comments</comments>
<pubDate>Tue, 31 Oct 2017 15:28:04 +0000</pubDate>
<dc:creator><![CDATA[Ryan Barrett]]></dc:creator>
<category><![CDATA[Uncategorized]]></category>
<guid isPermaLink="false">https://snarfed.org/2017-10-31_25851</guid>
<description><![CDATA[]]></description>
<content:encoded><![CDATA[<p><a href="https://i0.wp.com/snarfed.org/w/wp-content/uploads/2017/10/IMG_20171030_200005.jpg?ssl=1"><img class="shadow size-full" data-wpid="272" src="https://i0.wp.com/snarfed.org/w/wp-content/uploads/2017/10/IMG_20171030_200005.jpg?w=696&#038;ssl=1" data-recalc-dims="1"></a></p>
]]></content:encoded>
<wfw:commentRss>https://snarfed.org/2017-10-31_25851/feed</wfw:commentRss>
<slash:comments>38</slash:comments>
</item>
<item>
<title></title>
<link>https://snarfed.org/2017-10-25_25812</link>
<comments>https://snarfed.org/2017-10-25_25812#respond</comments>
<pubDate>Wed, 25 Oct 2017 17:01:49 +0000</pubDate>
<dc:creator><![CDATA[Ryan Barrett]]></dc:creator>
<category><![CDATA[Uncategorized]]></category>
<guid isPermaLink="false">https://snarfed.org/?p=25812</guid>
<description><![CDATA[Whoa. Microsoft is killing the Kinect. It&#8217;s been a while coming, I guess, but still. End of an era!]]></description>
<content:encoded><![CDATA[<p>Whoa. <a href="https://www.fastcodesign.com/90147868/exclusive-microsoft-has-stopped-manufacturing-the-kinect">Microsoft is killing the Kinect.</a> It&#8217;s been a while coming, I guess, but still. End of an era!</p>
]]></content:encoded>
<wfw:commentRss>https://snarfed.org/2017-10-25_25812/feed</wfw:commentRss>
<slash:comments>0</slash:comments>
</item>
<item>
<title></title>
<link>https://snarfed.org/2017-10-23_25792</link>
<comments>https://snarfed.org/2017-10-23_25792#respond</comments>
<pubDate>Mon, 23 Oct 2017 20:06:25 +0000</pubDate>
<dc:creator><![CDATA[Ryan Barrett]]></dc:creator>
<category><![CDATA[Uncategorized]]></category>
<guid isPermaLink="false">https://snarfed.org/?p=25792</guid>
<description><![CDATA[Nothing as good will ever be built again, so let us make proper use of what we&#8217;ve got. &#160;&#160; &#8211; Clerk of Works, Chatsworth Estate, ~1960 &#160;&#160;&#160; via Deborah Cavendish, Duchess of Devonshire, The Estate &#160;&#160;&#160; via Stewart Brand, How Buildings Learn]]></description>
<content:encoded><![CDATA[<blockquote>
<p>Nothing as good will ever be built again, so let us make proper use of what we&#8217;ve got.</p>
</blockquote>
<p>&nbsp;&nbsp; &#8211; Clerk of Works, <a href="https://en.wikipedia.org/wiki/Chatsworth_House#Estate">Chatsworth Estate</a>, ~1960<br />
&nbsp;&nbsp;&nbsp; via <a href="https://en.wikipedia.org/wiki/Deborah_Cavendish,_Duchess_of_Devonshire">Deborah Cavendish, Duchess of Devonshire</a>, <em><a href="https://books.google.com/books/about/The_Estate.html?id=DuVCNQAACAAJ">The Estate</a></em><br />
&nbsp;&nbsp;&nbsp; via <a href="https://books.google.com/books?id=zkgRgdVN2GIC">Stewart Brand, <em>How Buildings Learn</em></a></p>
]]></content:encoded>
<wfw:commentRss>https://snarfed.org/2017-10-23_25792/feed</wfw:commentRss>
<slash:comments>0</slash:comments>
</item>
<item>
<title>Bridgy Fed</title>
<link>https://snarfed.org/2017-10-22_bridgy-fed</link>
<comments>https://snarfed.org/2017-10-22_bridgy-fed#respond</comments>
<pubDate>Mon, 23 Oct 2017 04:01:46 +0000</pubDate>
<dc:creator><![CDATA[Ryan Barrett]]></dc:creator>
<category><![CDATA[Uncategorized]]></category>
<guid isPermaLink="false">https://snarfed.org/?p=25785</guid>
<description><![CDATA[I&#8217;m launching a new side project! Bridgy Fed connects your IndieWeb site with federated social networks like Mastodon and Hubzilla. If your site can send and receive webmentions, Bridgy Fed can federate it with the fediverse! I originally discovered the IndieWeb after I started connecting this web site with social networks like Facebook and Twitter. [&#8230;]]]></description>
<content:encoded><![CDATA[<p class="right third">
<a href="https://fed.brid.gy/">
<img src="/bridgy_fed.png"></a></p>
<p>I&#8217;m launching a new side project! <a href="https://fed.brid.gy/">Bridgy Fed</a> connects your <a href="https://indieweb.org/">IndieWeb</a> site with federated social networks like <a href="https://joinmastodon.org/">Mastodon</a> and <a href="https://project.hubzilla.org/">Hubzilla</a>. If your site can send and receive <a href="https://indieweb.org/webmention">webmentions</a>, Bridgy Fed can federate it with the <a href="https://en.wikipedia.org/wiki/Fediverse">fediverse</a>!</p>
<p>I originally discovered the IndieWeb after I started <a href="/2011-07-27_facebook_app_for_ostatus">connecting this web site</a> with social networks like Facebook and Twitter. That led to <a href="https://brid.gy/">Bridgy</a>, which has grown bigger than I care to admit. I still have that desire to bridge different communities and networks, and Bridgy Fed is just the latest incarnation. Give it a whirl, kick the tires, and <a href="https://github.com/snarfed/bridgy-fed/issues">let me know what you think</a>!</p>
]]></content:encoded>
<wfw:commentRss>https://snarfed.org/2017-10-22_bridgy-fed/feed</wfw:commentRss>
<slash:comments>0</slash:comments>
</item>
<item>
<title></title>
<link>https://snarfed.org/2017-10-15_25692</link>
<comments>https://snarfed.org/2017-10-15_25692#comments</comments>
<pubDate>Sun, 15 Oct 2017 13:43:58 +0000</pubDate>
<dc:creator><![CDATA[Ryan Barrett]]></dc:creator>
<category><![CDATA[Uncategorized]]></category>
<guid isPermaLink="false">https://snarfed.org/?p=25692</guid>
<description><![CDATA[That feeling when you Google something obscure and only find your own old stuff.]]></description>
<content:encoded><![CDATA[<p>That feeling when you Google something obscure and only find your own old stuff.</p>
]]></content:encoded>
<wfw:commentRss>https://snarfed.org/2017-10-15_25692/feed</wfw:commentRss>
<slash:comments>21</slash:comments>
</item>
<item>
<title></title>
<link>https://snarfed.org/2017-10-10_25648</link>
<comments>https://snarfed.org/2017-10-10_25648#comments</comments>
<pubDate>Tue, 10 Oct 2017 18:11:51 +0000</pubDate>
<dc:creator><![CDATA[Ryan Barrett]]></dc:creator>
<category><![CDATA[Uncategorized]]></category>
<guid isPermaLink="false">https://snarfed.org/2017-10-10_25648</guid>
<description><![CDATA[Found this front page of the Feb. 18, 1976 New Nigerian blowing down our street in San Francisco.]]></description>
<content:encoded><![CDATA[<p>Found this front page of the Feb. 18, 1976 <a href="http://www.newnigeriannewspaper.com/">New Nigerian</a> blowing down our street in San Francisco.</p>
<p><img class="size-full shadow" data-wpid="271" src="https://i2.wp.com/snarfed.org/w/wp-content/uploads/2017/10/IMG_20171010_110709_484.jpg?w=696&#038;ssl=1" data-recalc-dims="1"></p>
]]></content:encoded>
<wfw:commentRss>https://snarfed.org/2017-10-10_25648/feed</wfw:commentRss>
<slash:comments>11</slash:comments>
</item>
<item>
<title></title>
<link>https://snarfed.org/2017-10-08_25636</link>
<comments>https://snarfed.org/2017-10-08_25636#comments</comments>
<pubDate>Sun, 08 Oct 2017 17:07:08 +0000</pubDate>
<dc:creator><![CDATA[Ryan Barrett]]></dc:creator>
<category><![CDATA[Uncategorized]]></category>
<guid isPermaLink="false">https://snarfed.org/2017-10-08_25636</guid>
<description><![CDATA[Seeing more and more cards with red edges these days. Different networks, issuing banks, etc. What&#8217;s the secret?]]></description>
<content:encoded><![CDATA[<p>Seeing more and more cards with red edges these days. Different networks, issuing banks, etc. What&#8217;s the secret?</p>
<p><img class="shadow size-full" data-wpid="270" src="https://i2.wp.com/snarfed.org/w/wp-content/uploads/2017/10/IMG_20171008_115950_71101.jpg?w=696&#038;ssl=1" data-recalc-dims="1"></p>
]]></content:encoded>
<wfw:commentRss>https://snarfed.org/2017-10-08_25636/feed</wfw:commentRss>
<slash:comments>6</slash:comments>
</item>
<item>
<title></title>
<link>https://snarfed.org/2017-09-28_25563</link>
<comments>https://snarfed.org/2017-09-28_25563#comments</comments>
<pubDate>Thu, 28 Sep 2017 13:48:30 +0000</pubDate>
<dc:creator><![CDATA[Ryan Barrett]]></dc:creator>
<category><![CDATA[Uncategorized]]></category>
<guid isPermaLink="false">https://snarfed.org/2017-09-28_25563</guid>
<description><![CDATA[Morning, @karlthefog.]]></description>
<content:encoded><![CDATA[<p>Morning, <a href="https://twitter.com/KarlTheFog">@karlthefog</a>.</p>
<p><img class="shadow size-full" data-wpid="264" src="https://i0.wp.com/snarfed.org/w/wp-content/uploads/2017/09/IMG_20170928_064056_754.jpg?w=696&#038;ssl=1" data-recalc-dims="1"></p>
]]></content:encoded>
<wfw:commentRss>https://snarfed.org/2017-09-28_25563/feed</wfw:commentRss>
<slash:comments>14</slash:comments>
</item>
<item>
<title></title>
<link>https://snarfed.org/2017-09-12_25492</link>
<comments>https://snarfed.org/2017-09-12_25492#comments</comments>
<pubDate>Tue, 12 Sep 2017 20:09:12 +0000</pubDate>
<dc:creator><![CDATA[Ryan Barrett]]></dc:creator>
<category><![CDATA[Uncategorized]]></category>
<guid isPermaLink="false">https://snarfed.org/2017-09-12_25492</guid>
<description><![CDATA[new business cards &#x1f602;]]></description>
<content:encoded><![CDATA[<p>new business cards <img src="https://s.w.org/images/core/emoji/2.3/72x72/1f602.png" alt="😂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p><img data-wpid="262" src="https://i0.wp.com/snarfed.org/w/wp-content/uploads/2017/09/IMG_20170912_131414_767.jpg?w=696&#038;ssl=1" class="size-full shadow" data-recalc-dims="1"></p>
]]></content:encoded>
<wfw:commentRss>https://snarfed.org/2017-09-12_25492/feed</wfw:commentRss>
<slash:comments>84</slash:comments>
</item>
</channel>
</rss>

Loading…
Cancel
Save