
#!/bin/bash

# DISCLAIMER: this script has been started solely by me (sayan01)
# and has had contributions from others like Nicholas-Baron and
# juanCortelezzi (refer to contributors on github for details)
# this script is not based off of any other work by anyone else,
# nor is it made in association with anyone else, regardless of
# their claims.

# Dependencies: mpv youtube-dl fzf  gnu-grep

# NOTE:  if you don't have gnu grep, you can replace grep with rg

# Explain usage
function usage () {
    printf "usage: yt\n"
    printf "    -h        help\n"
    printf "    -c        channels/subscriptions\n"
    printf "    -s query  search\n"
    printf "    -m        music mode (audio only) [dont use with -r]\n"
    printf "    -a        autoplay mode (play next video automatically)\n"
    printf "  nothing     use defaults (search from prompt)\n"
    printf "\n"
    printf "add channel names to the file %s to show them\n" "$sublistpath"
    printf "in yt -c option. First word should be channel url, optionally\n"
    printf "followed by tab and then anything else (channel name/description)\n"
    printf "channels not in sublist can be viewed by typing their url in the prompt\n"
    printf "\n"
    printf "example file format:\n"
    printf "markrober       Mark Rober\n"
    printf "vsauce1         VSauce          Michael Steven's Channel\n"
    printf "BlackGryph0n    Black Gryph0n   Gabriel Brown signs stuff\n"
    printf "TomScottGo      Tom Scott\n"
    printf "danielthrasher  Daniel Thrasher\n"
    exit 0
}

# Use defaults if no arguments provided
useDefaults="f"
if [[ ${#} -eq 0 ]]; then
    useDefaults="t"
fi

# Available flags
optstring=":s:cgrhma"

defcmd="fzf"
defaction="s"

# Defaults
promptcmd="$defcmd"
action="$defaction"
autoplay="f"
query=""
mpv_options=""

max_resolution="480"
max_fps="30"
ignore_codec=""
ytdlformats="bestvideo"

if [ -n "$max_resolution" ] ; then
  ytdlformats+="[height<=?$max_resolution]"
fi
if [ -n "$max_fps" ] ; then
  ytdlformats+="[fps<=?$max_fps]"
fi
if [ -n "$ignore_codec" ] ; then
  ytdlformats+="[vcodec!=$ignore_codec]"
fi
ytdlformats+="+bestaudio/best"

# Subscription list
mkdir -p "${HOME:-}/.config/yt"
sublistpath="${HOME:-}/.config/yt/sublist"
sublist=""
[ -f "$sublistpath" ] && sublist=$(cat "$sublistpath")

# If not using defaults, search for flags
if [[ $useDefaults = "f" ]]; then
    while getopts ${optstring} arg; do
        case "${arg}" in
            s)
                # Search in YouTube for a query
                action="s"
                query="${OPTARG}" ;;
            c)
                # Search in subscriptions for a specific channel
                action="c"
                query="${OPTARG}" ;;
                        m)
                # Make the mpv headless
                mpv_options+="--no-video" ;;
            a)
                # Enable autoplay mode
                autoplay="t" ;;
            h)
                # Display help / usage
                usage ;;
            \?|:)
                # Wrong args -> exit with explanation of usage
                echo "invalid option: -${OPTARG}"
                echo
                usage
                exit 1 ;;
        esac
    done
fi

# If no query is set with flags, then ask for one
if [ -z "$query" ]; then
    # Ask for a channel
    if [[ $action = "c" ]]; then
        query=$($promptcmd --print-query <<< "$sublist" | tail -n1)
        query=$(echo "$query" | awk '{print $1}')
    else
        # Ask for a query
        echo -n "Search: "
        read -r query
    fi
fi

# Program cancelled -> exit
if [ -z "$query" ]; then exit; fi

# Clean query / channel
query=$(sed \
  -e 's|+|%2B|g'\
  -e 's|#|%23|g'\
  -e 's|&|%26|g'\
  -e 's| |+|g' <<< "$query")

# If channel, look for channel vids
if [[ $action = "c" ]]; then
    response=$(curl -s "https://www.youtube.com/c/$query/videos" |\
      sed "s/{\"gridVideoRenderer/\n\n&/g" |\
      sed "s/}]}}}]}}/&\n\n/g" |\
      awk -v ORS="\n\n" '/gridVideoRenderer/')

    # If unable to fetch the YouTube results page, inform and exit
    if ! grep -q "gridVideoRenderer" <<< "$response"; then echo "unable to fetch yt"; exit 1; fi

    # Regex expression to match video entries from YouTube channel page
    # Get the list of videos and their ids to ids
    ids=$(awk -F '[""]' '{print $6 "\t" $50;}' <<< "$response" | grep "^\S")

    # URL prefix for videos
    videolink="https://youtu.be/"

    # Convert ids to array for autoplay
    readarray -t id_array <<< "$ids"
    current_index=0

    # Prompt the results to user infinitely until they exit (escape)
    while true; do
      if [[ $autoplay = "t" ]] && [[ $current_index -gt 0 ]]; then
        # In autoplay mode, automatically play the next video
        if [[ $current_index -lt ${#id_array[@]} ]]; then
          choice=$(echo "${id_array[$current_index]}" | cut -d'	' -f2)
          id=$(echo "${id_array[$current_index]}" | cut -d'	' -f1)
          echo -e "Autoplaying: $choice\t($id)"
        else
          echo "End of playlist reached"
          exit
        fi
      else
        # Manual selection mode
        choice=$(echo -e "$ids" | cut -d'	' -f2 | $promptcmd) # Don't show id
        if [ -z "$choice" ]; then exit; fi	# If esc-ed then exit
        id=$(echo -e "$ids" | grep -Fwm1 "$choice" | cut -d'	' -f1) # Get id of choice
        echo -e "$choice\t($id)"
        # Find the index of selected video for autoplay continuation
        for i in "${!id_array[@]}"; do
          if [[ "${id_array[$i]}" == "$id"* ]]; then
            current_index=$i
            break
          fi
        done
      fi

      case $id in
          ???????????)
            mpv "$videolink$id" $mpv_options --ytdl-format="$ytdlformats"
            current_index=$((current_index + 1))
            ;;
          *) exit ;;
      esac

      # If not in autoplay mode, continue prompting
      if [[ $autoplay = "f" ]]; then
        continue
      fi
    done
else
    # If in search, show query result vids
    response="$(curl -s "https://www.youtube.com/results?search_query=$query" |\
      sed 's|\\.||g')"
    # If unable to fetch the YouTube results page, inform and exit
    if ! grep -q "script" <<< "$response"; then echo "unable to fetch yt"; exit 1; fi
    # Regex expression to match video and playlist entries from YouTube result page
    vgrep='"videoRenderer":{"videoId":"\K.{11}".+?"text":".+?[^\\](?=")'
    pgrep='"playlistRenderer":{"playlistId":"\K.{34}?","title":{"simpleText":".+?[^\"](?=")'
    # Grep the id and title
    # Return them in format id (type) title
    getresults() {
        grep -oP "$1" <<< "$response" |\
          awk -F\" -v p="$2" '{ print $1 "\t" p " " $NF}'
    }
    # Get the list of videos/playlists and their ids in videoids and playlistids
    videoids=$(getresults "$vgrep")
    playlistids=$(getresults "$pgrep" "(playlist)")
    # If there are playlists or videos, append them to list
    [ -n "$playlistids" ] && ids="$playlistids\n"
    [ -n "$videoids" ] && ids="$ids$videoids"
    # URL prefix for videos and playlists
    videolink="https://youtu.be/"
    playlink="https://youtube.com/playlist?list="

    # Convert ids to array for autoplay
    readarray -t id_array <<< "$(echo -e "$ids")"
    current_index=0

    # Prompt the results to user infinitely until they exit (escape)
    while true; do
        if [[ $autoplay = "t" ]] && [[ $current_index -gt 0 ]]; then
          # In autoplay mode, automatically play the next video
          if [[ $current_index -lt ${#id_array[@]} ]]; then
            choice=$(echo "${id_array[$current_index]}" | cut -d'	' -f2)
            id=$(echo "${id_array[$current_index]}" | cut -d'	' -f1)
            echo -e "Autoplaying: $choice\t($id)"
          else
            echo "End of playlist reached"
            exit
          fi
        else
          # Manual selection mode
          choice=$(echo -e "$ids" | cut -d'	' -f2 | $promptcmd) # Don't show id
          if [ -z "$choice" ]; then exit; fi	# If esc-ed then exit
          id=$(echo -e "$ids" | grep -Fwm1 "$choice" | cut -d'	' -f1) # Get id of choice
          echo -e "$choice\t($id)"
          # Find the index of selected video for autoplay continuation
          for i in "${!id_array[@]}"; do
            if [[ "${id_array[$i]}" == "$id"* ]]; then
              current_index=$i
              break
            fi
          done
        fi

        case $id in
            # 11 digit id = video
            ???????????)
              mpv "$videolink$id" $mpv_options --ytdl-format="$ytdlformats"
              current_index=$((current_index + 1))
              ;;
            # 34 digit id = playlist
            ??????????????????????????????????)
              mpv "$playlink$id" $mpv_options --ytdl-format="$ytdlformats"
              current_index=$((current_index + 1))
              ;;
            *) exit ;;
        esac

        # If not in autoplay mode, continue prompting
        if [[ $autoplay = "f" ]]; then
          continue
        fi
    done
fi
