#!/bin/bash
#
# podcast-downloader
# Version: 1.0.1
# Description: A tool to download podcasts from an RSS feed and
#              optionally tag their metadata with album name.
#
# Copyright (C) 2025 Willem L. Middelkoop (mail@willem.com)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
#
# For more information, visit: https://source.willem.com/podcast-downloader/


# Default configurations
PODCAST_DIR=~/Podcasts            # Default directory for podcast downloads
RSS_FEED=""                       # RSS feed URL (to be provided by the user)
ALBUM_NAME=""                     # Default album name is empty (skip tagging if not provided)
TMP_DIR=$(mktemp -d)              # Temporary directory for processing

# Function to print usage information
usage() {
    echo "Usage: $0 -u <RSS_FEED_URL> [-d <DOWNLOAD_DIR>] [-a <ALBUM_NAME>]"
    echo
    echo "Options:"
    echo "  -u  Specify the RSS feed URL (required)."
    echo "  -d  Specify the directory for downloads (default: ~/Podcasts)."
    echo "  -a  Specify the album name for tagging (default: no album name, skipping tagging)."
    echo "  -h  Show this help message."
    exit 1
}

# Parse command-line arguments
while getopts ":u:d:a:h" opt; do
    case $opt in
        u) RSS_FEED="$OPTARG" ;;
        d) PODCAST_DIR="$OPTARG" ;;
        a) ALBUM_NAME="$OPTARG" ;;
        h) usage ;;
        \?) echo "Invalid option: -$OPTARG" >&2; usage ;;
        :) echo "Option -$OPTARG requires an argument." >&2; usage ;;
    esac
done

# Check if RSS feed URL is provided
if [ -z "$RSS_FEED" ]; then
    echo "Error: RSS feed URL is required."
    usage
fi

# Ensure the download directory exists
mkdir -p "$PODCAST_DIR"

# Start of the process, output to standard output
echo "Podcast Downloader started at $(date)"
echo "Downloading from RSS feed: $RSS_FEED"

# Download the RSS feed
RSS_FILE="$TMP_DIR/rss_feed.xml"
wget -q -O "$RSS_FILE" "$RSS_FEED"

if [ $? -ne 0 ]; then
    echo "Error: Failed to download RSS feed. Check the URL."
    echo "Aborting."
    rm -rf "$TMP_DIR"
    exit 1
fi

# Extract MP3 (or other media file) URLs from the RSS feed
# =======================================================
# The following line uses `awk` to parse the RSS feed and extract URLs 
# from the <enclosure> tags. These tags typically specify the location 
# of podcast media files (e.g., .mp3, .m4a).
#
# - The field separator (-F) is set to match the portion of the <enclosure> tag 
#   up to the 'url="' attribute, effectively splitting each matching line into fields.
#   The second field ($2) contains everything after 'url="'.
# - We use `split($2, arr, "\"")` to break this field at the next double quote (")
#   so that `arr[1]` contains the actual URL.
# - The `awk` command ensures that only the URL portion is output for further processing.
#
# Note: This parsing assumes that the <enclosure> tag contains a valid 'url' attribute 
# and follows common XML formatting conventions. This approach may fail on malformed or 
# unusual RSS feeds, so adjustments may be needed for specific edge cases.

# Parse the RSS feed for media URLs and process them one at a time
awk -F'enclosure[^>]*url="' '/<enclosure/ {split($2, arr, "\""); print arr[1]}' "$RSS_FILE" | while read -r URL; do

	# Extract 10 lines before and after the URL for context
	# 'grep -B 10 -A 10' is used here to extract the 10 lines before and after the MP3 URL
    # This provides context to locate the title and pubDate that are typically
    # located near the MP3 URL in the RSS feed, but this assumption is not guaranteed.
    CONTEXT=$(grep -B 10 -A 10 "$URL" "$RSS_FILE")

	# Get or construct the following fields from the context
    TITLE=$(echo "$CONTEXT" | awk -F'<title>|</title>' '/<title>/ {print $2; exit}')
    PUB_DATE=$(echo "$CONTEXT" | awk -F'<pubDate>|</pubDate>' '/<pubDate>/ {print $2; exit}')
    DATE=$(date -d "$PUB_DATE" +'%Y-%m-%d' 2>/dev/null)
	
	# Escape title to remove problematic characters and normalize
	ESCAPED_TITLE=$(echo "$TITLE" | sed 's/[^a-zA-Z0-9_-]/_/g')

	# Remove (possible) double underscores
	ESCAPED_TITLE=$(echo "$ESCAPED_TITLE" | sed 's/__/_/g')

	# Remove (possible) trailing underscores
	ESCAPED_TITLE=$(echo "$ESCAPED_TITLE" | sed 's/_$//')

	# Extract the file extension from the URL (default to mp3 if not found)
	EXTENSION=$(echo "$URL" | grep -oP '\.\w+$' | sed 's/^\.//')
	EXTENSION=${EXTENSION:-mp3}

	# Construct the filename with the dynamic extension
	FILENAME="${DATE}_${ESCAPED_TITLE}.${EXTENSION}"


    if [ ! -f "$PODCAST_DIR/$FILENAME" ]; then
        echo "Downloading: $FILENAME"
        wget -q -O "$PODCAST_DIR/$FILENAME" "$URL"

        if [ $? -eq 0 ]; then
            echo "Download completed: $FILENAME"

            # Apply album tagging only if ALBUM_NAME is provided
            if [ -n "$ALBUM_NAME" ] && command -v id3v2 &> /dev/null; then
                id3v2 -A "$ALBUM_NAME" "$PODCAST_DIR/$FILENAME"
                echo "Tagged album as '$ALBUM_NAME' for $FILENAME"
            elif [ -n "$ALBUM_NAME" ]; then
                echo "id3v2 not installed; skipping album tagging for $FILENAME"
            else
                echo "No album name provided; skipping album tagging for $FILENAME"
            fi
        else
            echo "Failed to download: $URL"
        fi
    else
        echo "Already exists: $FILENAME"
    fi
done

# Clean up
rm -rf "$TMP_DIR"

# End of the process
echo "Podcast Downloader finished at $(date)"
