JSON Feed support, closes #55
This commit is contained in:
parent
f2154e4468
commit
e7398b9b6f
Binary file not shown.
After Width: | Height: | Size: 934 B |
16
itsb.xml
16
itsb.xml
|
@ -39,28 +39,28 @@
|
|||
<region>Canada</region>
|
||||
<type>Rail, aviation, marine, pipeline</type>
|
||||
<feed lang="English" format="rss" type="rail" id="tsb-en-rail">
|
||||
<link>http://www.bst-tsb.gc.ca/eng/fils-feeds/TSB%20Rail.xml</link>
|
||||
<link>https://www.bst-tsb.gc.ca/eng/fils-feeds/TSB%20Rail.xml</link>
|
||||
</feed>
|
||||
<feed lang="French" format="rss" type="rail" id="tsb-fr-rail">
|
||||
<link>http://www.bst-tsb.gc.ca/fra/fils-feeds/BST%20Rail.xml</link>
|
||||
<link>https://www.bst-tsb.gc.ca/fra/fils-feeds/BST%20Rail.xml</link>
|
||||
</feed>
|
||||
<feed lang="English" format="rss" type="aviation" id="tsb-en-aviation">
|
||||
<link>http://www.bst-tsb.gc.ca/eng/fils-feeds/TSB%20Air.xml</link>
|
||||
<link>https://www.bst-tsb.gc.ca/eng/fils-feeds/TSB%20Air.xml</link>
|
||||
</feed>
|
||||
<feed lang="French" format="rss" type="aviation" id="tsb-fr-aviation">
|
||||
<link>http://www.bst-tsb.gc.ca/fra/fils-feeds/BST%20Aviation.xml</link>
|
||||
<link>https://www.bst-tsb.gc.ca/fra/fils-feeds/BST%20Aviation.xml</link>
|
||||
</feed>
|
||||
<feed lang="English" format="rss" type="marine" id="tsb-en-marine">
|
||||
<link>http://www.bst-tsb.gc.ca/eng/fils-feeds/TSB%20Marine.xml</link>
|
||||
<link>https://www.bst-tsb.gc.ca/eng/fils-feeds/TSB%20Marine.xml</link>
|
||||
</feed>
|
||||
<feed lang="French" format="rss" type="marine" id="tsb-fr-marine">
|
||||
<link>http://www.bst-tsb.gc.ca/fra/fils-feeds/BST%20Marine.xml</link>
|
||||
<link>https://www.bst-tsb.gc.ca/fra/fils-feeds/BST%20Marine.xml</link>
|
||||
</feed>
|
||||
<feed lang="English" format="rss" type="pipeline" id="tsb-en-pipeline">
|
||||
<link>http://www.bst-tsb.gc.ca/eng/fils-feeds/TSB%20Pipeline.xml</link>
|
||||
<link>https://www.bst-tsb.gc.ca/eng/fils-feeds/TSB%20Pipeline.xml</link>
|
||||
</feed>
|
||||
<feed lang="French" format="rss" type="pipeline" id="tsb-fr-pipeline">
|
||||
<link>http://www.bst-tsb.gc.ca/fra/fils-feeds/BST%20Pipeline.xml</link>
|
||||
<link>https://www.bst-tsb.gc.ca/fra/fils-feeds/BST%20Pipeline.xml</link>
|
||||
</feed>
|
||||
</source>
|
||||
|
||||
|
|
1
itsb.xsd
1
itsb.xsd
|
@ -310,6 +310,7 @@
|
|||
<xs:enumeration value="rss" />
|
||||
<xs:enumeration value="atom" />
|
||||
<xs:enumeration value="rdf" />
|
||||
<xs:enumeration value="json" />
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
# Atom->JSON Feed converter
|
||||
# Expects xmltodict JSON output as input, outputs a JSON Feed 1.1 feed backwards-compatible with 1.0
|
||||
# Does not support entries without links.
|
||||
# Required variables:
|
||||
# $feed_url: Absolute URL to the resulting JSON Feed
|
||||
|
||||
def ensure_string: if type == "array" then .[0] else . end | if type == "object" then .["#text"] else . end;
|
||||
def ensure_array: (. // []) | if type == "array" then . else [.] end;
|
||||
def find_links(rel): [(.link // []) | if type == "array" then .[] else . end | select(.["@rel"] == rel)];
|
||||
def parse_authors: [
|
||||
((.author | ensure_array) + (.contributor | ensure_array))[]
|
||||
| . as $author
|
||||
| {"name": .name}
|
||||
| if $author.uri then .url = $author.uri else . end
|
||||
];
|
||||
|
||||
.feed | . as $feed | {
|
||||
"version": "https://jsonfeed.org/version/1.1",
|
||||
"title": (.title | ensure_string),
|
||||
"home_page_url": find_links("alternate")[0]["@href"],
|
||||
"feed_url": $feed_url,
|
||||
"description": (.description | ensure_string // ""),
|
||||
"expired": false,
|
||||
"authors": parse_authors,
|
||||
"items": [
|
||||
.entry | ensure_array[] | select(.link) | . as $entry | {
|
||||
"id": .id,
|
||||
"title": (.title | ensure_string),
|
||||
"url": find_links("alternate")[0]["@href"],
|
||||
"date_published": (.published // .updated),
|
||||
"date_modified": .updated,
|
||||
"authors": parse_authors,
|
||||
"tags": [
|
||||
.category | ensure_array[] | .["@term"]
|
||||
],
|
||||
"attachments": [
|
||||
find_links("enclosure")[] | {
|
||||
"url": .["@href"],
|
||||
"size_in_bytes": .["@length"],
|
||||
"mime_type": .["@type"]
|
||||
}
|
||||
],
|
||||
"content_html": (.content | ensure_array[0] | ensure_string // "")
|
||||
}
|
||||
# Optional summary
|
||||
| if $entry.summary then .summary = ($entry.summary | ensure_string // "") else . end
|
||||
# Optional external URL
|
||||
| if ($entry|find_links("related")[0]) then .external_url = ($entry|find_links("related")[0]["@href"]) else . end
|
||||
]
|
||||
}
|
||||
# Optional icon
|
||||
| if $feed.icon then .icon = $feed.icon else . end
|
|
@ -0,0 +1,43 @@
|
|||
# RSS->JSON Feed converter
|
||||
# Expects xmltodict JSON output as input, outputs a JSON Feed 1.1 feed backwards-compatible with 1.0
|
||||
# Does not support RSS items without links.
|
||||
# Required variables:
|
||||
# $feed_url: Absolute URL to the resulting JSON Feed
|
||||
|
||||
def ensure_string: if type == "array" then .[0] else . end | if type == "object" then .["#text"] else . end;
|
||||
def ensure_array: (. // []) | if type == "array" then . else [.] end;
|
||||
|
||||
.rss.channel | . as $channel | {
|
||||
"version": "https://jsonfeed.org/version/1.1",
|
||||
"title": (.title | ensure_string),
|
||||
"home_page_url": (.link | ensure_string),
|
||||
"feed_url": $feed_url,
|
||||
"description": (.description | ensure_string),
|
||||
"expired": false,
|
||||
"items": [
|
||||
.item | ensure_array[] | select(.link) | . as $item | {
|
||||
"id": (.guid // .link | ensure_string),
|
||||
"title": .title,
|
||||
"url": .link,
|
||||
"content_html": (.description // "" | ensure_string),
|
||||
"tags": [
|
||||
.category | ensure_array[] | ensure_string
|
||||
],
|
||||
"attachments": [
|
||||
.enclosure | ensure_array[] | {
|
||||
"url": .["@url"],
|
||||
"size_in_bytes": .["@length"],
|
||||
"mime_type": .["@type"]
|
||||
}
|
||||
]
|
||||
}
|
||||
# Optional publication date
|
||||
| if $item.pubDate then .date_published = ($item.pubDate | strptime("%a, %d %b %Y %T %z") | mktime | todateiso8601) else . end
|
||||
# Optional author
|
||||
| if $item.author then .author = {"name": $item.author} | .authors = [.author] else . end
|
||||
]
|
||||
}
|
||||
# Optional language
|
||||
| if $channel.language then .language = $channel.language else . end
|
||||
# Optional icon
|
||||
| if $channel.image then .icon = $channel.image.url else . end
|
156
xslt/itsb.xsl
156
xslt/itsb.xsl
|
@ -6,9 +6,10 @@
|
|||
xmlns:itsb="http://tilde.town/~lucidiot/itsb/">
|
||||
<xsl:output method="xml" />
|
||||
|
||||
<!--
|
||||
Preprocesses the itsb.xml file to autocomplete with converted feeds.
|
||||
-->
|
||||
<!-- Preprocesses the itsb.xml file to autocomplete with converted feeds. -->
|
||||
|
||||
<!-- Caution: Final slash is required. -->
|
||||
<xsl:variable name="itsbRoot" select="'https://tilde.town/~lucidiot/itsb/'" />
|
||||
|
||||
<xsl:template match="node()|@*">
|
||||
<xsl:copy>
|
||||
|
@ -24,58 +25,119 @@
|
|||
<xsl:variable name="type" select="@type" />
|
||||
|
||||
<!-- This is an Atom feed and there is no RSS feed: add a conversion -->
|
||||
<xsl:if test="@format = 'atom' and not(../itsb:feed[((not(@lang) and not($lang)) or @lang = $lang) and ((not(@type) and not($type)) or @type = $type) and (@format = 'rss' or @format = 'rdf')])">
|
||||
<feed>
|
||||
<xsl:if test="@lang">
|
||||
<xsl:attribute name="lang">
|
||||
<xsl:value-of select="@lang" />
|
||||
<xsl:choose>
|
||||
<xsl:when test="@format = 'atom' and not(../itsb:feed[((not(@lang) and not($lang)) or @lang = $lang) and ((not(@type) and not($type)) or @type = $type) and (@format = 'rss' or @format = 'rdf')])">
|
||||
<feed format="rss">
|
||||
<xsl:if test="@lang">
|
||||
<xsl:attribute name="lang">
|
||||
<xsl:value-of select="@lang" />
|
||||
</xsl:attribute>
|
||||
</xsl:if>
|
||||
<xsl:if test="@type">
|
||||
<xsl:attribute name="type">
|
||||
<xsl:value-of select="@type" />
|
||||
</xsl:attribute>
|
||||
</xsl:if>
|
||||
<xsl:attribute name="id">
|
||||
<xsl:value-of select="@id" />
|
||||
<xsl:text>-rss</xsl:text>
|
||||
</xsl:attribute>
|
||||
</xsl:if>
|
||||
<xsl:if test="@type">
|
||||
<xsl:attribute name="type">
|
||||
<xsl:value-of select="@type" />
|
||||
</xsl:attribute>
|
||||
</xsl:if>
|
||||
<xsl:attribute name="id">
|
||||
<xsl:value-of select="@id" />
|
||||
<xsl:text>-2rss</xsl:text>
|
||||
</xsl:attribute>
|
||||
<xsl:attribute name="format">rss</xsl:attribute>
|
||||
|
||||
<xsl:choose>
|
||||
<xsl:when test="itsb:output">
|
||||
<shell>
|
||||
<xsl:text>cat $DIR/feeds/</xsl:text>
|
||||
<xsl:value-of select="itsb:output/text()" />
|
||||
</shell>
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<curl>
|
||||
<url>
|
||||
<xsl:value-of select="itsb:link/text()" />
|
||||
</url>
|
||||
</curl>
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
|
||||
<xml2json />
|
||||
<jq path="convert/atom2rss.jq" />
|
||||
<json2xml />
|
||||
|
||||
<output>
|
||||
<xsl:text>_rss/</xsl:text>
|
||||
<xsl:choose>
|
||||
<xsl:when test="itsb:output">
|
||||
<xsl:value-of select="itsb:output/text()" />
|
||||
<shell>
|
||||
<xsl:text>cat $DIR/feeds/</xsl:text>
|
||||
<xsl:value-of select="itsb:output/text()" />
|
||||
</shell>
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<xsl:value-of select="@id" />
|
||||
<xsl:text>.xml</xsl:text>
|
||||
<curl>
|
||||
<url>
|
||||
<xsl:value-of select="itsb:link/text()" />
|
||||
</url>
|
||||
</curl>
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
</output>
|
||||
</feed>
|
||||
</xsl:if>
|
||||
|
||||
<xml2json />
|
||||
<jq path="convert/atom2rss.jq" />
|
||||
<json2xml />
|
||||
|
||||
<output>
|
||||
<xsl:text>_rss/</xsl:text>
|
||||
<xsl:choose>
|
||||
<xsl:when test="itsb:output">
|
||||
<xsl:value-of select="itsb:output/text()" />
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<xsl:value-of select="@id" />
|
||||
<xsl:text>.xml</xsl:text>
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
</output>
|
||||
</feed>
|
||||
|
||||
<xsl:call-template name="jsonfeed" />
|
||||
</xsl:when>
|
||||
|
||||
<xsl:when test="@format = 'rss'">
|
||||
<xsl:call-template name="jsonfeed" />
|
||||
</xsl:when>
|
||||
</xsl:choose>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template name="jsonfeed">
|
||||
<feed format="json">
|
||||
<xsl:if test="@lang">
|
||||
<xsl:attribute name="lang">
|
||||
<xsl:value-of select="@lang" />
|
||||
</xsl:attribute>
|
||||
</xsl:if>
|
||||
<xsl:if test="@type">
|
||||
<xsl:attribute name="type">
|
||||
<xsl:value-of select="@type" />
|
||||
</xsl:attribute>
|
||||
</xsl:if>
|
||||
<xsl:attribute name="id">
|
||||
<xsl:value-of select="@id" />
|
||||
<xsl:text>-json</xsl:text>
|
||||
</xsl:attribute>
|
||||
|
||||
<xsl:choose>
|
||||
<xsl:when test="itsb:output">
|
||||
<shell>
|
||||
<xsl:text>cat $DIR/feeds/</xsl:text>
|
||||
<xsl:value-of select="itsb:output/text()" />
|
||||
</shell>
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<curl>
|
||||
<url>
|
||||
<xsl:value-of select="itsb:link/text()" />
|
||||
</url>
|
||||
</curl>
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
|
||||
<xml2json />
|
||||
<jq>
|
||||
<xsl:attribute name="path">
|
||||
<xsl:text>convert/</xsl:text>
|
||||
<xsl:value-of select="@format" />
|
||||
<xsl:text>2jsonfeed.jq</xsl:text>
|
||||
</xsl:attribute>
|
||||
<arg name="feed_url">
|
||||
<xsl:value-of select="$itsbRoot" />
|
||||
<xsl:text>feeds/_json/</xsl:text>
|
||||
<xsl:value-of select="@id" />
|
||||
<xsl:text>.json</xsl:text>
|
||||
</arg>
|
||||
</jq>
|
||||
<output>
|
||||
<xsl:text>_json/</xsl:text>
|
||||
<xsl:value-of select="@id" />
|
||||
<xsl:text>.json</xsl:text>
|
||||
</output>
|
||||
</feed>
|
||||
</xsl:template>
|
||||
</xsl:stylesheet>
|
||||
|
|
Loading…
Reference in New Issue