A first attempt at supporting podcasts. iTunes RSS feed, layouts, parameters etc. included.

This commit is contained in:
Fredrik Jonsson 2019-05-13 09:08:29 +02:00
commit 9d40eac2d6
12 changed files with 252 additions and 33 deletions

View file

@ -25,6 +25,7 @@ This is originally a port of the [Zen](https://www.drupal.org/project/zen) theme
* Meta tags and JSON-LD
* Multilingual (i18n)
* Normalize CSS
* Podcast
* Responsive design
* RSS and JSON feeds with full content
* Search with DuckDuckGo
@ -129,14 +130,36 @@ params:
microUsername: "" # Your micro.blog username.
mobileMenu: true # Turn on a mobile menu on small screens.
piwikSiteId: # Matamo site id
piwikTrackerUrl: "" # Matamo url, schemaless and no slash on end (example.com/matamo).
piwikTrackerUrl: "" # Matamo url, schemaless and no slash on end (example.org/matamo).
poweredby: true # Show powered by hugo in footer
search: true # Site search with DuckDuckGo.
searchSize: 20 # Search field size, default 20.
sidebar: true # Show a sidebar to the right
submitted: true # Show author and date information for a post.
podcast:
title: # * Feed title, defaults to site title (iTunes).
description: # * Feed description/summary, defaults to site description (iTunes).
image: # * Feed image, place inside assets directory (iTunes).
category:
name: # * Feed category (iTunes).
subcategories: [] # Feed sub category (iTunes).
explicit: false/true # Feed explicit settting, default to false (iTunes).
author: # Feed author (iTunes).
owner:
name: # Feed owner name (iTunes).
email: # Feed owner e-mail (iTunes).
lang: # Feed language, defaults to site language (iTunes).
block: no/yes # Block the feed from iTunes, default to no (iTunes).
complete: no/yes # Set the feed as complete, defaults to no (iTunes).
type: episodic/serial # Podcast type, defaults to episodic (iTunes).
newfeed: # Are you moving? Set the new feed url here (iTunes).
cdn: # CDN url, no slash on end (https://cdn.example.org).
preload: none/metadata/auto # Set on the HTML5 audio tag, defaults to "metadata".
```
The site will work without setting any of the podcast parameters but your podcast feed will not be accepted by iTunes. At a minimum you need to set the first four, title, description, image and category name. Read more in the podcast section below.
## Multilingual
@ -183,6 +206,37 @@ If "lastmod" is set in the frontmatter on a post that value will be used in the
With "lastmod" set a date section will also appear at the bottom of post telling the reader the created and modification dates.
## Podcast
The Zen theme supports podcasting.
* RSS feed with all the needed iTunes tags.
* Single and full layouts with HTML 5 audio player.
* Archetype with required parameters.
Here follow all the possible podcast frontmatter parameters. Only the first two is mandatory and are in the podcast archetype.
```
podcast:
mp3: # * The path to the mp3 file,
duration: # * Episode duration, e.g 1:04:02 (iTunes).
image:
src: # Episode image src, place inside the assets directory (iTunes).
alt: # Alt text for the image, explain what is on the image.
width: # Image width in the article, defaults to 250px.
class: # Image wrapper class.
explicit: true/false # Episode explicit setting, default to false (iTunes).
episode: # Episode number (iTunes).
episodeType: full/trailer/bonus # Episode type, defaults to full (iTunes).
season: # Episode season (iTunes).
block: # Block the episode from iTunes, default to no (iTunes).
```
* [Apple Podcasts categories](https://help.apple.com/itc/podcasts_connect/#/itc9267a2f12)
* [Apple - A podcaster’s guide to RSS](https://help.apple.com/itc/podcasts_connect/#/itcb54353390)
## Shortcodes
### Contact
@ -201,6 +255,7 @@ Zen comes with a improved version of the built in "figure" shortcut.
* If width and height is not set the real dimensions of the image will be used.
* If only width or height is set the other value will be proportionally calculated.
### Clear
Break float.
@ -215,6 +270,25 @@ blablabla # Displayed left of the image.
blablabla # Displayed below of the image.
```
### Audio and Video
```
{{< audio src="/audio/audio.mp3" class="something" >}}
{{< video src="/video/video.mp4" class="something" >}}
```
Possible parameters are:
* src
* class
* preload (none/metadata/auto, default metadata)
* width (only video)
The audio and video tags will be wrapped with a figure tag.
### Wrapper
A simple, but useful, shortcode to wrap content in a div with a class. The content will be run through "markdownify".

5
archetypes/micro.md Normal file
View file

@ -0,0 +1,5 @@
---
title: "{{ replace .TranslationBaseName "-" " " | humanize }}"
date: {{ .Date }}
---

8
archetypes/podcast.md Normal file
View file

@ -0,0 +1,8 @@
---
title: "{{ replace .TranslationBaseName "-" " " | humanize }}"
date: {{ .Date }}
podcast:
mp3:
duration:
---

View file

@ -161,46 +161,56 @@ article {
}
figure {
@include respond-to(s) {
display: table;
margin: 0;
img {
display: block;
&.image {
figcaption {
@include respond-to(s) {
display: table-caption;
caption-side: bottom;
}
h4,
p {
margin: 0;
}
}
&.left {
@include padding(0 .5 .5 0);
margin: 0;
float: left;
clear: both;
@include respond-to(s) {
display: table;
figcaption {
img {
display: block;
}
&.left {
@include padding(0 .5 .5 0);
margin: 0;
float: left;
clear: both;
figcaption {
@include padding(0 .5 .5 0);
}
}
}
&.right {
@include padding(0 0 .5 .5);
margin: 0;
float: right;
clear: both;
figcaption {
&.right {
@include padding(0 0 .5 .5);
margin: 0;
float: right;
clear: both;
figcaption {
@include padding(0 0 .5 .5);
}
}
}
}
}
figcaption {
@include respond-to(s) {
display: table-caption;
caption-side: bottom;
}
h4,
p {
margin: 0;
&.podcast {
audio {
width: 95%;
}
}
}

View file

@ -0,0 +1,12 @@
{{ $src := (.Params.podcast.mp3 | absURL) -}}
{{ if .Site.Params.Podcast.cdn -}}
{{ $src = add $.Site.Params.Podcast.cdn (.Params.podcast.mp3 | relURL) -}}
{{ end -}}
<figure class="podcast">
<audio controls preload="{{ .Site.Params.Podcast.preload | default "metadata" }}"><source src="{{ $src }}" type="audio/mpeg"></audio>
<figcaption><a href="{{ $src }}">{{ .Title }}</a></figcaption>
</figure>
{{ with .Params.podcast.image }}
{{ $resource := resources.Get .src }}{{ $size := "250x" }}{{ with .width }}{{ $size = (add (string .) "x") }}{{ end }}{{ $image := $resource.Resize $size -}}
<figure class="image {{ .class | default "right" }}"><img src="{{ $image.RelPermalink }}" width="{{ .width | default "250" }}" alt="{{ .alt }}"></figure>
{{ end -}}

10
layouts/podcast/full.html Normal file
View file

@ -0,0 +1,10 @@
<article class="{{ with .Section }}section-{{ . | urlize }} {{ end }}full-view">
<header>
<h1 class="title {{ if $.Param "submitted" | default false }}title-submitted{{ end }}"><a href="{{ .RelPermalink }}">{{ .Title }}</a></h1>
{{ if $.Param "submitted" | default false }}{{ partial "submitted.html" . }}{{ end -}}
</header>
{{ partial "podcast.html" . -}}
<div class="content">
{{ .Content }}
</div>
</article>

64
layouts/podcast/rss.xml Normal file
View file

@ -0,0 +1,64 @@
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd">
<channel>
<title>{{ .Site.Params.Podcast.Title | default .Site.Title }}</title>
<link>{{ .Permalink }}</link>
<description>{{ .Site.Params.Podcast.Description | default .Site.Params.Description }}</description>
<generator>Hugo -- gohugo.io</generator>
<language>{{ .Site.Params.Podcast.Lang | default .Site.LanguageCode }}</language>
{{ with .Site.Author.email -}}
<managingEditor>{{ . }}{{ with $.Site.Author.name }} ({{ . }}){{end}}</managingEditor>
{{ end -}}
{{ with .Site.Author.email -}}
<webMaster>{{ . }}{{ with $.Site.Author.name }} ({{ . }}){{end}}</webMaster>
{{ end -}}
{{ with .Site.Copyright -}}
<copyright>{{ . }}</copyright>
{{ end -}}
{{ if not .Date.IsZero -}}
<lastBuildDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</lastBuildDate>
{{ end -}}
{{ with .OutputFormats.Get "RSS" -}}
{{ printf "<atom:link href=%q rel=\"self\" type=%q />" .Permalink .MediaType | safeHTML }}
{{ end -}}
{{ with .Site.Params.Podcast.Image }}{{ $image := resources.Get . }}<itunes:image href="{{ $image.Permalink }}" />{{ end }}
{{ with .Site.Params.Podcast.Category -}}
<itunes:category {{ if .name }}text="{{ .name }}"{{end}}>
{{ range .subcategories }}<itunes:category text="{{ . }}" />{{ end }}
</itunes:category>
{{ end -}}
{{ with .Site.Params.Podcast.Owner -}}
<itunes:owner>
{{ with .name }}<itunes:name>{{ . }}</itunes:name>{{ end }}
{{ with .email }}<itunes:email>{{ . }}</itunes:email>{{ end }}
</itunes:owner>
{{ end -}}
{{ with .Site.Params.Podcast.Description }}<itunes:summary>{{ . }}</itunes:summary>{{ end }}
{{ with .Site.Params.Podcast.Author }}<itunes:author>{{ . }}</itunes:author>{{ end }}
{{ with .Site.Params.Podcast.Type }}<itunes:type>{{ . }}</itunes:type>{{ end }}
{{ with .Site.Params.Podcast.Block }}<itunes:block>{{ . }}</itunes:block>{{ end }}
{{ with .Site.Params.Podcast.Complete }}<itunes:complete>{{ . }}</itunes:complete>{{ end }}
{{ with .Site.Params.Podcast.NewFeed }}<itunes:new-feed-url>{{ . }}</itunes:new-feed-url>{{ end }}
<itunes:explicit>{{ if .Site.Params.Podcast.Explicit }}true{{ else }}false{{ end }}</itunes:explicit>
{{ range .Data.Pages }}
<item>
<title>{{ .Title }}</title>
<itunes:title>{{ .Title }}</itunes:title>
<link>{{ .Permalink }}</link>
<guid>{{ .Permalink }}</guid>
<pubDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</pubDate>
{{ with .Site.Author.email -}}
<author>{{ . }}{{ with $.Site.Author.name }} ({{ . }}){{ end }}</author>
{{ end -}}
<description>{{ .Content | html }}</description>
{{ with .Params.podcast.mp3 }}{{ $file_stat := os.Stat (add "/static" (. | relURL)) }}<enclosure url="{{ if $.Site.Params.Podcast.cdn }}{{ add $.Site.Params.Podcast.cdn (. | relURL) }}{{ else }}{{ . | absURL }}{{ end }}" length="{{ $file_stat.Size }}" type="audio/mpeg" />{{ end }}
{{ with .Params.podcast.duration }}<itunes:duration>{{ . }}</itunes:duration>{{ end }}
{{ with .Params.podcast.image.src }}{{ $image := resources.Get . }}<itunes:image href="{{ $image.Permalink }}" />{{ end }}
{{ with .Params.podcast.episode }}<itunes:episode>{{ . }}</itunes:episode>{{ end }}
{{ with .Params.podcast.episodeType }}<itunes:episodeType>{{ . }}</itunes:episodeType>{{ end }}
{{ with .Params.podcast.season }}<itunes:season>{{ . }}</itunes:season>{{ end }}
{{ with .Params.podcast.block }}<itunes:block>{{ . }}</itunes:block>{{ end }}
<itunes:explicit>{{ if .Params.podcast.explicit }}true{{ else }}false{{ end }}</itunes:explicit>
</item>
{{ end }}
</channel>
</rss>

View file

@ -0,0 +1,24 @@
{{ define "meta" }}
{{- partial "single_meta.html" . -}}
{{- partial "opengraph_meta.html" . -}}
{{ end }}
{{ define "header" }}
{{- partial "single_json_ld.html" . -}}
{{ end }}
{{ define "main" -}}
<main class="main layout__main">
<article class="{{ with .Section }}section-{{ . | urlize }} {{ end }}single-view">
<header>
<h1 class="title {{ if $.Param "submitted" | default false }}title-submitted{{ end }}">{{ .Title }}</h1>
{{ if $.Param "submitted" | default false }}{{ partial "submitted.html" . }}{{ end -}}
{{ if .Params.tags }}{{ partial "tags.html" . }}{{ end -}}
</header>
{{ partial "podcast.html" . -}}
<div class="content">
{{ .Content }}
</div>
</article>
</main>
{{ end }}

View file

@ -0,0 +1,6 @@
<figure {{ with .Get "class" }}class="{{ . }}"{{ end }}>
<audio controls preload="{{ .Get "preload" | default "metadata" }}">
{{ with .Get "src" }}<source src="{{ . | relURL }}" type="audio/mpeg">{{ end }}
{{ with .Get "caption" }}<figcaption>{{ . }}</figcaption>{{ end }}
</audio>
</figure>

View file

@ -25,9 +25,9 @@
{{ $height := (mul ($.Scratch.Get "realheight") (div (add ($.Scratch.Get "width") 0.0) ($.Scratch.Get "realwidth"))) -}}
{{ $.Scratch.Set "height" (math.Round $height) -}}
{{ end -}}
<figure {{ with .Get "class" }}class="{{ . }}"{{ end }}>
<figure class="image {{ .Get "class" | default "" }}">
{{ with .Get "link" }}<a href="{{ . }}">{{ end -}}
<img src="{{ $.Get "src" | relURL }}" {{ if or (.Get "alt") (.Get "caption") }}alt="{{ with .Get "alt" }}{{ . }}{{ else }}{{ .Get "caption" }}{{ end }}"{{ end }}{{ with $.Scratch.Get "width" }} width="{{ . }}"{{ end }}{{ with $.Scratch.Get "height" }} height="{{ . }}"{{ end }}>
<img src="{{ .Get "src" | relURL }}" {{ if or (.Get "alt") (.Get "caption") }}alt="{{ with .Get "alt" }}{{ . }}{{ else }}{{ .Get "caption" }}{{ end }}"{{ end }}{{ with $.Scratch.Get "width" }} width="{{ . }}"{{ end }}{{ with $.Scratch.Get "height" }} height="{{ . }}"{{ end }}>
{{- if .Get "link" }}</a>{{ end }}
{{ if or (or (.Get "title") (.Get "caption")) (.Get "attr") -}}
<figcaption>{{ if isset .Params "title" }}

View file

@ -0,0 +1,6 @@
<figure {{ with .Get "class" }}class="{{ . }}"{{ end }}>
<video controls preload="{{ .Get "preload" | default "metadata" }}" {{ with .Get "width" }}width="{{ . }}"{{ end }}>
{{ with .Get "src" }}<source src="{{ . | relURL }}" type="video/mp4">{{ end }}
</video>
{{ with .Get "caption" }}<figcaption>{{ . }}</figcaption>{{ end }}
</figure>

View file

@ -3,8 +3,8 @@ description = "A solid base for your custom Hugo theme with libsass and gulp sup
homepage = "https://github.com/frjo/hugo-theme-zen"
license = "GPLv2"
licenselink = "http://www.gnu.org/licenses/old-licenses/gpl-2.0.html"
tags = ["multilingual", "responsive", "blog", "search", "micro"]
features = ["multilingual", "responsive", "grid", "sass", "gulp", "search", "matamo", "jsonfeed", "subtheme"]
tags = ["multilingual", "responsive", "blog", "search", "micro", "podcast"]
features = ["multilingual", "responsive", "grid", "sass", "search", "matamo", "jsonfeed", "subtheme", "podcast"]
min_version = 0.47
[author]