#!/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"

# 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/"

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

# Default configuration values
podcast_title=""
podcast_description=""
podcast_author=""
podcast_email=""
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" ;;
        email)       podcast_email="$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[@]}")

mapfile -d '' -t files < <(
    find -L media/ "${find_args[@]}" -printf '%T@\t%p\0' |
    sort -zn "${sort_args[@]}" |
    while IFS=$'\t' read -r -d '' mtime file; do
        printf '%s\0' "$file"
    done
)

# 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>
<description><![CDATA[$podcast_description]]></description>
<author>$podcast_author</author>
<image>
    <url>$url_base/avatar.png</url>
    <title>$podcast_title</title>
    <link>$url_base</link>
</image>
<language>$podcast_language</language>
<link>$url_base/$rss_file</link>
<lastBuildDate>$generated</lastBuildDate>
<managingEditor>$podcast_email ($podcast_author)</managingEditor>
<itunes:image href="$url_base/avatar.png" />
<itunes:owner>
    <itunes:name>$podcast_author</itunes:name>
    <itunes:email>$podcast_email</itunes:email>
</itunes:owner>
<itunes:category text="$(printf '%s' "$podcast_category" | escape_html)" />
<itunes:explicit>$podcast_explicit</itunes:explicit>
<itunes:author>$podcast_author</itunes:author>
<itunes:email>$podcast_email</itunes:email>
<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"

    # Use file modification time as the episode date
    mtime=$(stat -c %Y "$url")
    display_date=$(date -d "@$mtime" +"$date_format")
    rss_date=$(date -R -d "@$mtime")
    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")

title_html=$(printf '%s' "$title" | escape_html)

# HTML template
cat >> "$html_tmp" <<EOF
<article>
<h2>$title_html</h2>
<div class="article-time"><time>$display_date</time></div>
<audio controls preload="none">
<source src="$url" type="audio/mpeg">
</audio>
<div class="synopsis">$synopsis_html</div>
</article>
EOF

# RSS template
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"
