update with build script to configure base path and generate files dynamically
This commit is contained in:
parent
6f63c1a734
commit
6915aac092
10 changed files with 250 additions and 20 deletions
189
build.sh
Executable file
189
build.sh
Executable file
|
|
@ -0,0 +1,189 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
if [ $# -ne 1 ]; then
|
||||
echo "Usage: $0 <baseURL>" >&2
|
||||
echo " <baseURL>: absolute URL prefix for mp3 enclosures" >&2
|
||||
echo " e.g. https://example.com/podcast" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BASE_URL="${1%/}"
|
||||
POSTS_DIR="posts"
|
||||
HTML_OUT="index.html"
|
||||
XML_OUT="podcast.xml"
|
||||
|
||||
PODCAST_TITLE="Guardian Project Podcast"
|
||||
PODCAST_DESC="A small sample podcast from Guardian Project covering decentralized web, privacy-preserving measurement, and content provenance for human rights."
|
||||
PODCAST_AUTHOR="Guardian Project"
|
||||
PODCAST_LINK="index.html"
|
||||
|
||||
# Extract a single frontmatter field from a markdown file
|
||||
fm_get() {
|
||||
awk -v key="$2" '
|
||||
/^---[[:space:]]*$/ { fm++; if (fm == 2) exit; next }
|
||||
fm == 1 {
|
||||
i = index($0, ":")
|
||||
if (i == 0) next
|
||||
k = substr($0, 1, i-1)
|
||||
v = substr($0, i+1)
|
||||
sub(/^[[:space:]]+/, "", k); sub(/[[:space:]]+$/, "", k)
|
||||
sub(/^[[:space:]]+/, "", v); sub(/[[:space:]]+$/, "", v)
|
||||
if (k != key) next
|
||||
if ((substr(v,1,1)=="\"" && substr(v,length(v))=="\"") || \
|
||||
(substr(v,1,1)=="\x27" && substr(v,length(v))=="\x27")) {
|
||||
v = substr(v, 2, length(v)-2)
|
||||
}
|
||||
print v
|
||||
exit
|
||||
}
|
||||
' "$1"
|
||||
}
|
||||
|
||||
# Escape text for safe use in HTML/XML element content and attributes
|
||||
esc() {
|
||||
local s="$1"
|
||||
# In bash 5.2+, & in the replacement means "matched text", so escape as \&.
|
||||
s="${s//&/\&}"
|
||||
s="${s//</\<}"
|
||||
s="${s//>/\>}"
|
||||
s="${s//\"/\"}"
|
||||
printf '%s' "$s"
|
||||
}
|
||||
|
||||
# Newest-first list of posts (filenames start with YYYY-MM-DD so sort works)
|
||||
mapfile -t POSTS < <(find "$POSTS_DIR" -maxdepth 1 -name '*.md' | sort -r)
|
||||
|
||||
if [ "${#POSTS[@]}" -eq 0 ]; then
|
||||
echo "No markdown files found in $POSTS_DIR/" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- podcast.xml header ---
|
||||
{
|
||||
cat <<EOF
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rss version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd">
|
||||
<channel>
|
||||
<title>$(esc "$PODCAST_TITLE")</title>
|
||||
<link>$(esc "$PODCAST_LINK")</link>
|
||||
<language>en-us</language>
|
||||
<description>$(esc "$PODCAST_DESC")</description>
|
||||
<itunes:author>$(esc "$PODCAST_AUTHOR")</itunes:author>
|
||||
<itunes:summary>$(esc "$PODCAST_DESC")</itunes:summary>
|
||||
<itunes:explicit>no</itunes:explicit>
|
||||
EOF
|
||||
} > "$XML_OUT"
|
||||
|
||||
# --- index.html header ---
|
||||
{
|
||||
cat <<EOF
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>$(esc "$PODCAST_TITLE")</title>
|
||||
<style>
|
||||
body { font-family: system-ui, sans-serif; max-width: 720px; margin: 2em auto; padding: 0 1em; line-height: 1.5; color: #222; }
|
||||
h1 { border-bottom: 2px solid #333; padding-bottom: .3em; }
|
||||
.episode { border: 1px solid #ddd; border-radius: 6px; padding: 1em 1.2em; margin: 1.2em 0; background: #fafafa; }
|
||||
.episode h2 { margin: 0 0 .3em 0; font-size: 1.15em; }
|
||||
.meta { color: #666; font-size: .85em; margin-bottom: .6em; }
|
||||
audio { width: 100%; margin-top: .5em; }
|
||||
.feed-link { font-size: .9em; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>$(esc "$PODCAST_TITLE")</h1>
|
||||
<p>$(esc "$PODCAST_DESC") <span class="feed-link"><a href="podcast.xml">RSS feed</a></span></p>
|
||||
|
||||
EOF
|
||||
} > "$HTML_OUT"
|
||||
|
||||
# --- per-post items ---
|
||||
for post in "${POSTS[@]}"; do
|
||||
title=$(fm_get "$post" title)
|
||||
date_raw=$(fm_get "$post" date)
|
||||
desc=$(fm_get "$post" description)
|
||||
[ -z "$desc" ] && desc=$(fm_get "$post" summary)
|
||||
file=$(fm_get "$post" file)
|
||||
voices=$(fm_get "$post" voices)
|
||||
keywords=$(fm_get "$post" keywords)
|
||||
explicit=$(fm_get "$post" explicit)
|
||||
[ -z "$explicit" ] && explicit="no"
|
||||
|
||||
# Duration string is whichever of duration/length looks like HH:MM[:SS].
|
||||
# The two fields are used inconsistently across existing posts.
|
||||
dur_field=$(fm_get "$post" duration)
|
||||
len_field=$(fm_get "$post" length)
|
||||
if [[ "$dur_field" =~ ^[0-9]+:[0-9]+(:[0-9]+)?$ ]]; then
|
||||
duration="$dur_field"
|
||||
elif [[ "$len_field" =~ ^[0-9]+:[0-9]+(:[0-9]+)?$ ]]; then
|
||||
duration="$len_field"
|
||||
else
|
||||
duration=""
|
||||
fi
|
||||
|
||||
if [ -z "$file" ]; then
|
||||
echo "Warning: no file: field in $post, skipping" >&2
|
||||
continue
|
||||
fi
|
||||
mp3_path="$POSTS_DIR/$file"
|
||||
if [ ! -f "$mp3_path" ]; then
|
||||
echo "Warning: missing mp3 $mp3_path, skipping $post" >&2
|
||||
continue
|
||||
fi
|
||||
filesize=$(stat -c %s "$mp3_path")
|
||||
|
||||
date_short=$(printf '%s' "$date_raw" | awk '{print $1}')
|
||||
# Strip trailing numeric timezone (some posts use "-500" which date(1) rejects)
|
||||
date_norm=$(printf '%s' "$date_raw" | sed -E 's/[[:space:]]+[+-]?[0-9]{3,4}$//')
|
||||
pubdate=$(date -R -d "$date_norm" 2>/dev/null || date -R -d "$date_short")
|
||||
|
||||
guid="${file%.mp3}"
|
||||
|
||||
# podcast.xml item — enclosure is absolute, everything else is plain text
|
||||
{
|
||||
printf '\n <item>\n'
|
||||
printf ' <title>%s</title>\n' "$(esc "$title")"
|
||||
printf ' <description>%s</description>\n' "$(esc "$desc")"
|
||||
printf ' <pubDate>%s</pubDate>\n' "$pubdate"
|
||||
printf ' <enclosure url="%s/posts/%s" length="%s" type="audio/mpeg"/>\n' \
|
||||
"$BASE_URL" "$file" "$filesize"
|
||||
printf ' <guid isPermaLink="false">%s</guid>\n' "$(esc "$guid")"
|
||||
[ -n "$duration" ] && printf ' <itunes:duration>%s</itunes:duration>\n' "$duration"
|
||||
[ -n "$voices" ] && printf ' <itunes:author>%s</itunes:author>\n' "$(esc "$voices")"
|
||||
printf ' <itunes:explicit>%s</itunes:explicit>\n' "$(esc "$explicit")"
|
||||
[ -n "$keywords" ] && printf ' <itunes:keywords>%s</itunes:keywords>\n' "$(esc "$keywords")"
|
||||
printf ' </item>\n'
|
||||
} >> "$XML_OUT"
|
||||
|
||||
# index.html article — audio src is a relative path into posts/
|
||||
meta="$date_short"
|
||||
[ -n "$duration" ] && meta="$meta · $duration"
|
||||
[ -n "$voices" ] && meta="$meta · Voices: $(esc "$voices")"
|
||||
|
||||
{
|
||||
printf '<article class="episode">\n'
|
||||
printf ' <h2>%s</h2>\n' "$(esc "$title")"
|
||||
printf ' <div class="meta">%s</div>\n' "$meta"
|
||||
printf ' <p>%s</p>\n' "$(esc "$desc")"
|
||||
printf ' <audio controls preload="none" src="%s/%s"></audio>\n' "$POSTS_DIR" "$file"
|
||||
printf '</article>\n\n'
|
||||
} >> "$HTML_OUT"
|
||||
done
|
||||
|
||||
# --- close files ---
|
||||
cat >> "$XML_OUT" <<EOF
|
||||
|
||||
</channel>
|
||||
</rss>
|
||||
EOF
|
||||
|
||||
cat >> "$HTML_OUT" <<EOF
|
||||
</body>
|
||||
</html>
|
||||
EOF
|
||||
|
||||
echo "Wrote $XML_OUT and $HTML_OUT (baseURL: $BASE_URL)"
|
||||
Loading…
Add table
Add a link
Reference in a new issue