#!/usr/bin/env bash
set -euo pipefail
shopt -u patsub_replacement
umask 022

# Exit with an error message
die() { echo "Error: $1" >&2 && exit 1; }

# Check required external commands
command -v ffprobe >/dev/null || die "ffprobe required"
command -v sed     >/dev/null || die "sed required"
command -v stat    >/dev/null || die "stat required"

# Project files
readonly html_file="index.html"
readonly rss_file="rss.xml"
readonly site_conf="site.conf"
readonly item_template="item.html"

# Temp files
html_tmp=$(mktemp)
rss_tmp=$(mktemp)

# Verify required files and directories exist
[[ -f "$site_conf" ]] || die "missing $site_conf"
[[ -d media        ]] || die "missing media/"

# Load item template, or use built-in default
if [[ -f "$item_template" ]]; then
    item_template_content=$(<"$item_template")
else
    item_template_content='<article>
<h2>{{title}}</h2>
<div class="article-time"><time>{{date}}</time></div>
<audio controls preload="none">
<source src="{{url}}" type="audio/mpeg">
</audio>
<div class="synopsis">{{synopsis}}</div>
</article>'
fi

# Deletes temp files
trap 'rm -f "$html_tmp" "$rss_tmp"' EXIT

# Default configuration values
podcast_title=""
podcast_description=""
podcast_author=""
podcast_language="en-GB"
podcast_category="Society & Culture"
podcast_explicit="false"
date_format="%d %b %Y"
recursive=false
order=reverse
url_base=""

# Load site configuration
while IFS=':' read -r k v; do
    k=$(xargs <<< "$k")
    v=$(xargs <<< "$v")
    case "$k" in
        title)       podcast_title="$v" ;;
        description) podcast_description="$v" ;;
        author)      podcast_author="$v" ;;
        language)    podcast_language="$v" ;;
        category)    podcast_category="$v" ;;
        explicit)    podcast_explicit="$v" ;;
        date_format) date_format="$v" ;;
        recursive)   recursive="${v,,}" ;;
        order)       order="${v,,}" ;;
        url)         url_base="$v" ;;
    esac
done < "$site_conf"

# Check the site.conf
[[ -n $podcast_title ]] || die "title missing from site.conf"
[[ -n $podcast_author ]] || die "author missing from site.conf"
[[ -n $url_base ]] || die "url missing from site.conf"

# Remove trailing slash from site URL
url_base="${url_base%/}"

# Timestamp inserted into generated pages
generated=$(date -Ru)

# Build sort options
sort_args=()
[[ "$order" != "forward" ]] && sort_args+=("-r")

# Read an MP3 metadata tag
meta() {
    ffprobe \
        -v error \
        -show_entries format_tags="$1" \
        -of default=noprint_wrappers=1:nokey=1 \
        "$2" 2>/dev/null || true
}

# Escape HTML special characters
escape_html() { sed -e 's/&/\&amp;/g' -e 's/</\&lt;/g' -e 's/>/\&gt;/g'; }

# Convert URLs in text into HTML links
linkify() { sed -E 's@(https?://[^[:space:]<]+)@<a href="\1">\1</a>@g'; }

# Build find command arguments
find_args=(-type f -iname "*.mp3")
[[ "$recursive" != "true" ]] && find_args=(-maxdepth 1 "${find_args[@]}")

# Find MP3 files and sort them by filename
mapfile -t files < <(
    find -L media/ "${find_args[@]}" |
    awk -F/ '{print $NF "\t" $0}' |
    sort "${sort_args[@]}" |
    cut -f2
)

# Abort if no MP3 files were found
[[ ${#files[@]} -gt 0 ]] || die "no mp3 files found"

# HTML render top
cat > "$html_tmp" <<EOF
<!DOCTYPE html>
<html lang="$podcast_language">
<head>
<title>$(printf '%s' "$podcast_title" | escape_html)</title>
<link rel="alternate" type="application/rss+xml" title="$(printf '%s' "$podcast_title" | escape_html)" href="$rss_file">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8">
$( [[ -f style.css ]] && echo '<link rel="stylesheet" href="style.css">' )
<!-- Generated: $generated -->
</head>
<body>
EOF

# Add head.html if it exists
[[ -f head.html ]] && cat head.html >> "$html_tmp"

# RSS render top
cat > "$rss_tmp" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/"
     xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xmlns:atom="http://www.w3.org/2005/Atom"
     version="2.0"
     xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"
     xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
     xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
     xmlns:podcast="https://podcastindex.org/namespace/1.0">
<channel>
<title>$(printf '%s' "$podcast_title" | escape_html)</title>
<image><url>$url_base/avatar.png</url></image>
<itunes:image href="$url_base/avatar.png" />
<itunes:category text="$(printf '%s' "$podcast_category" | escape_html)" />
<itunes:explicit>$podcast_explicit</itunes:explicit>
<description><![CDATA[$podcast_description]]></description>
<author>$(printf '%s' "$podcast_author" | escape_html)</author>
<language>$podcast_language</language>
<link>$url_base/$rss_file</link>
<lastBuildDate>$generated</lastBuildDate>
<atom:link href="$url_base/$rss_file" rel="self" type="application/rss+xml" />
EOF

# Render HTML and RSS entries
for url in "${files[@]}"; do
    base=$(basename "$url")
    echo "Processing: $base"

    # Require filenames to begin with YYYYMMDD
    [[ "$base" =~ ^[0-9]{8} ]] || die "bad filename: $base"
    y=${base:0:4}
    m=${base:4:2}
    d=${base:6:2}
    display_date=$(date -d "$y-$m-$d" +"$date_format")
    rss_date=$(date -R -d "$y-$m-$d")
    title=$(meta TITLE "$url")
    synopsis=$(meta DESCRIPTION "$url")
    [[ -n "$title" ]] || title="Untitled"

    # Escape HTML, convert URLs to links and preserve line breaks
    synopsis_html=$(
        printf '%s\n' "$synopsis" |
        escape_html |
        linkify |
        sed ':a;N;$!ba;s/\n/<br>/g'
    )
    rss_url="${url_base}/${url}"
    rss_length=$(stat -c%s "$url")

    # Render HTML item
    item="$item_template_content"
    item=${item//'{{title}}'/$title}
    item=${item//'{{date}}'/$display_date}
    item=${item//'{{url}}'/$url}
    item=${item//'{{siteurl}}'/$url_base}
    item=${item//'{{synopsis}}'/$synopsis_html}
    printf '%s\n' "$item" >> "$html_tmp"

cat >> "$rss_tmp" <<EOF
<item>
<title>$(printf '%s' "$title" | escape_html)</title>
<description><![CDATA[$synopsis]]></description>
<pubDate>$rss_date</pubDate>
<link>$rss_url</link>
<guid isPermaLink="true">$rss_url</guid>
<enclosure url="$rss_url" length="$rss_length" type="audio/mpeg" />
</item>
EOF
done

# Add foot.html if it exists
[[ -f foot.html ]] && cat foot.html >> "$html_tmp"

# Finishing HTML
cat >> "$html_tmp" <<EOF
</body>
</html>
EOF

# Finishing RSS
cat >> "$rss_tmp" <<EOF
</channel>
</rss>
EOF

chmod 644 "$rss_tmp" "$html_tmp"
mv "$rss_tmp" "$rss_file"
mv "$html_tmp" "$html_file"

# Done
echo "Done"
