#!/bin/bash
###############################################################################
# dlyt.sh
#
# YouTube → Jellyfin Downloader & Organizer
#
# Downloads recent YouTube videos from channels listed in channels.txt,
# organizes them into a Jellyfin-friendly folder structure, generates NFO files,
# converts thumbnails from WEBP to JPG, and cleans up unnecessary files.
#
# Features:
# - Downloads up to 5 videos per channel (configurable via --playlist-end)
# - Skips live, upcoming, long (>2h) videos, or titles containing "WAN"
# - Sorts episodes by upload_date
# - Creates Season 01 and Specials (shorts)
# - Generates tvshow.nfo with channel description
# - Converts thumbnails from WEBP → JPG
# - Cleans up info.json and description files after NFO creation
# - Removes orphaned or leftover files
# - Changes ownership to jellyfin for proper library access
#
# Dependencies:
# - yt-dlp
# - jq
# - ImageMagick (magick command)
# - Bash ≥4
###############################################################################

set -euo pipefail  # Abort on errors, unset variables are errors, pipeline failures propagate

# BASE DIRECTORIES
BASE="/dir/to/folder"    # Root for all downloads
YTDLP="$BASE/yt-dlp"    # Folder for downloads
CHANNELS="$BASE/channels.txt"    # List of channels

mkdir -p "$YTDLP"    # Create download folder if missing

sudo chown -R user:user "${YTDLP}"    # Temporarily give ownership to the user to allow writing



echo "===== yt-dlp download ====="

# Download videos with yt-dlp
yt-dlp \
  --ignore-errors \
  --no-overwrites \
  --continue \
  --restrict-filenames \
  --windows-filenames \
  --playlist-end 5 \
  \
  --dateafter now-10days \
  \
  --match-filter "!is_live & live_status!=is_upcoming & duration<7200 & title!*=WAN" \
  \
  --format "(bestvideo[ext=mp4][height<=1080]/bestvideo[height<=1080])+(bestaudio[ext=m4a]/bestaudio)/best[ext=mp4]/best" \
  --merge-output-format mp4 \
  \
  --write-info-json \
  --write-description \
  --write-thumbnail \
  --embed-chapters \
  --sponsorblock-mark all,-preview,-filler,-interaction \
  --download-archive "$YTDLP/downloaded.txt" \
  \
  --output "$YTDLP/%(uploader)s/%(title)s [%(id)s].%(ext)s" \
  \
  -a "$CHANNELS" || true



echo "===== Organizing channels ====="

# Folder layout for each channel:
# SHOW/                   <- Root folder for channel (e.g., "SciManDan")
# ├─ Season 01/           <- Regular episodes (longer than 240s)
# │  ├─ S01E01 - Title1 [ID].mp4
# │  ├─ S01E02 - Title2 [ID].mp4
# │  ├─ ...
# │  ├─ S01E01 - Title1 [ID].webp  <- optional, converted later to .jpg
# │  └─ S01E02 - Title2 [ID].webp
# ├─ Specials/             <- Shorts (<241s (=4min))
# │  ├─ S00E01 - Short1 [ID].mp4
# │  ├─ S00E02 - Short2 [ID].mp4
# │  └─ ...
# ├─ tvshow.nfo            <- Generated channel NFO
# ├─ poster.jpg            <- First JPG in channel, copied as poster
# └─ other leftover files   <- cleaned up by the script

# Iterate over each channel folder
find "$YTDLP" -mindepth 1 -maxdepth 1 -type d | while read -r SHOW; do
  mkdir -p "$SHOW/Season 01" "$SHOW/Specials"  # Create Season/Specials folders

  ep1=1  # Season 01 episode counter
  ep0=1  # Specials (shorts) episode counter

  # Sort episodes by upload_date: get date|jsonfile, sort by date, then cut jsonfile
  find "$SHOW" -maxdepth 1 -name "*.info.json" | while read -r JSON; do
    DATE=$(jq -r '.upload_date // "00000000"' "$JSON")  # Use default 00000000 if missing
    echo "$DATE|$JSON"                                  # Format: YYYYMMDD|jsonfile
  done | sort | cut -d'|' -f2 | while read -r JSON; do
    # Explanation of pipe:
    # 1. `jq` extracts upload_date from each .info.json
    # 2. `echo "$DATE|$JSON"` pairikrs date with filename
    # 3. `sort` sorts alphabetically → chronological because YYYYMMDD
    # 4. `cut -d'|' -f2` returns only the filename, in sorted order
    BASEFILE="${JSON%.info.json}"       # Base filename without extension
    MP4="$BASEFILE.mp4"                  # Corresponding video file
    [ ! -f "$MP4" ] && continue          # Skip if video missing

    TITLE=$(jq -r '.title' "$JSON")     # Video title
    DESC=$(jq -r '.description // ""' "$JSON")  # Video description (empty if missing)
    DATE=$(jq -r '.upload_date' "$JSON")        # Upload date
    ID=$(jq -r '.id' "$JSON")                    # YouTube video ID
    DURATION=$(jq -r '.duration // 0' "$JSON")  # Duration in seconds

    AIRED="${DATE:0:4}-${DATE:4:2}-${DATE:6:2}"   # Convert YYYYMMDD → YYYY-MM-DD

    # Determine Season / Specials based on duration
    if [ "$DURATION" -lt 241 ]; then
      SEASON=0
      EP=$(printf "%02d" "$ep0")                      # Zero-padded episode
      SAFE_TITLE=$(echo "$TITLE" | cut -c1-120)      # Truncate long titles to 120 chars
      DEST="$SHOW/Specials/S00E$EP - $SAFE_TITLE [$ID]"
      ep0=$((ep0+1))                                  # Increment specials counter
    else
      SEASON=1
      EP=$(printf "%02d" "$ep1")
      SAFE_TITLE=$(echo "$TITLE" | cut -c1-120)
      DEST="$SHOW/Season 01/S01E$EP - $SAFE_TITLE [$ID]"
      ep1=$((ep1+1))                                  # Increment season counter
    fi

    mkdir -p "$(dirname "$DEST")"                      # Ensure destination folder exists

    mv "$MP4" "$DEST.mp4"                              # Move video to organized folder

    # Move WEBP thumbnail to match episode name
    if [ -f "$BASEFILE.webp" ]; then
      mv "$BASEFILE.webp" "$DEST.webp"
    fi

    # Create episode NFO
cat > "$DEST.nfo" <<EOF
# Mapping of episode files:
# For each episode DEST:
# DEST.mp4   <- main video
# DEST.webp  <- original thumbnail (moved)
# DEST.jpg   <- converted thumbnail via ImageMagick
# DEST.nfo   <- episode metadata
#
# This ensures Jellyfin recognizes the episode with proper thumbnail and metadata.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<episodedetails>
  <title>$TITLE</title>
  <season>$SEASON</season>
  <episode>$((10#$EP))</episode>  # Convert to decimal, handle leading zeros
  <airikred>$AIRED</airikred>
  <premiered>$AIRED</premiered>
  <plot><![CDATA[$DESC]]></plot>
  <uniqueid type="youtube">$ID</uniqueid>
</episodedetails>
EOF

    rm -f "$BASEFILE.info.json" "$BASEFILE.description"   # Cleanup metadata
done



echo "===== Generating tvshow.nfo for $(basename "$SHOW") ====="

# tvshow.nfo & poster.jpg:
# - tvshow.nfo contains channel title, studio, and channel description (plot)
# - poster.jpg is copied from the first .jpg found in channel (episode thumbnail)
# 
# Jellyfin reads this for series overview in the library.

if [ ! -f "$SHOW/tvshow.nfo" ]; then

  # Attempt to grab channel description from first info.json
  CHANNEL_JSON=$(find "$SHOW" -maxdepth 1 -name "*[[]*.info.json" | head -n 1)
  if [ -n "$CHANNEL_JSON" ]; then
    SHOW_DESC=$(jq -r '.description // ""' "$CHANNEL_JSON")
  else
    SHOW_DESC=""
  fi

cat > "$SHOW/tvshow.nfo" <<EOF
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<tvshow>
<title>$(basename "$SHOW")</title>
<studio>YouTube</studio>
<plot><![CDATA[$SHOW_DESC]]></plot>
</tvshow>
EOF
fi

# Ensure poster.jpg exists for the channel
if [ ! -f "$SHOW/poster.jpg" ]; then
  THUMB=$(find "$SHOW" -type f -iname "*.jpg" | head -n 1)
  [ -n "$THUMB" ] && cp "$THUMB" "$SHOW/poster.jpg"
fi
done



echo "===== Removing old JPGs in channel root ====="

# Remove unnecessary JPGs from root (except poster.jpg)
for CHANNEL in "$YTDLP"/*; do
  [ -d "$CHANNEL" ] || continue
  find "$CHANNEL" -maxdepth 1 -type f -name "*.jpg" ! -name "poster.jpg" -exec rm -f {} +
done



echo "===== Converting WEBP thumbnails to JPG with ImageMagick ====="

# Conversion process:
# - Each *.webp file in the channel folder tree is converted to *.jpg
# - Original *.webp is deleted later
# - JPG filename matches video DEST, so Jellyfin can use it automatically
# Example:
# /SciManDan/Season 01/S01E01 - Title1 [ID].webp -> S01E01 - Title1 [ID].jpg

# Convert WEBP thumbnails to JPG
find "$YTDLP" -type f -name "*.webp" | while read -r WEBP; do
    JPG="${WEBP%.webp}.jpg"
    magick "$WEBP" "$JPG"  # magick CLI converts webp → jpg
done



echo "===== Removing WEBP files ====="

find "$YTDLP" -type f -name "*.webp" -delete  # Remove WEBP after conversion



echo "===== Cleaning up leftover channel metadata ====="

# Remove any leftover .info.json and .description files
for CHANNEL in "$YTDLP"/*; do
  [ -d "$CHANNEL" ] || continue
  find "$CHANNEL" -maxdepth 1 -type f \( \
    -name "*.info.json" -o \
    -name "*.description" \
  \) -exec rm -f {} +
done



echo "===== Cleaning orphaned NFO and JPG files ====="

# Delete any leftover orphaned NFO/JPG if MP4 is missing
for CHANNEL in "$YTDLP"/*; do
  [ -d "$CHANNEL" ] || continue
  find "$CHANNEL" -type f \( -name "*.info.json" -o -name "*.description" \) -exec rm -f {} +
done



echo "===== Change owner rights to Jellyfin ====="

sudo chown -R jellyfin:jellyfin "${YTDLP}"   # Final ownership for Jellyfin access

echo "===== ALL DONE ====="