#!/bin/sh

######################################
#> https://github.com/pystardust/ytfzf
######################################

#exit codes
#0: success
#1: a general error
#2: missing dependancy, invalid opt or argument
#3: invalid argument for opt, error with config

YTFZF_VERSION="1.2.0"


############################
#         Defaults         #
############################


#>reading the config file
config_dir=${YTFZF_CONFIG_DIR-$HOME/.config/ytfzf}
config_file=${YTFZF_CONFIG_FILE-$config_dir/conf.sh}
tmp_video_data_file="/tmp/ytfzf-subdata"
tmp_video_json_file="/tmp/ytfzf-subjson"
#source config file if exists
#it also checks to make sure that this is not a subprocess of another ytfzf script
[ -e "$config_file" ] && . "$config_file"

#for each environment variable, check if it's set in environment,
    #if set in environment, use that value
    #otherwise use the variable set in config, if that's not set, use the default value

#enable/disable history
enable_hist=${YTFZF_HIST-${enable_hist-1}}
#enable/disable search history
enable_search_hist=${YTFZF_SEARCH_HIST-${enable_search_hist-1}}
enable_search_hist_menu=${YTFZF_SEARCH_HIST_MENU-${enable_search_hist_menu-0}}
allow_empty_search_hist=${YTFZF_ALLOW_EMPTY_SEARCH_HIST-${allow_empty_search_hist-0}}
search_history_prompt=${YTFZF_SEARCH_HISTORY_PROMPT-${search_history_prompt-"> "}}
#enable/disable looping
enable_loop=${YTFZF_LOOP-${enable_loop-0}}
#enable/disable outputting current track to $current_file
enable_cur=${YTFZF_CUR-${enable_cur-1}}
#enable/disable notification
enable_noti=${YTFZF_NOTI-${enable_noti-0}}
#the cache directory
cache_dir=${YTFZF_CACHE-${cache_dir-$HOME/.cache/ytfzf}}
#video type preference (mp4/1080p, mp4/720p, etc..)
video_pref=${YTFZF_PREF-${video_pref-}}
#the menu to use instead of fzf when -D is specified
external_menu=${YTFZF_EXTMENU-${external_menu-dmenu -i -l 30 -p Search:}}
#number of columns (characters on a line) the external menu can have
#necessary for formatting text for external menus
external_menu_len=${YTFZF_EXTMENU_LEN-${external_menu_len-220}}
#player settings (players need to support streaming with youtube-dl)
#player to use for watching the video
video_player=${YTFZF_PLAYER-${video_player-mpv}}
#if YTFZF_PREF is specified, use this player instead
video_player_format=${YTFZF_PLAYER_FORMAT-${video_player_format-mpv --ytdl-format=}}
#player to use for audio only
audio_player=${YTFZF_AUDIO_PLAYER-${audio_player-mpv --no-video}}
#the command to use for displaying thumbnails
thumb_disp_method=${YTFZF_THUMB_DISP_METHOD-${thumb_disp_method-ueberzug}}
#Storing the argument and location for autogenerated subtitles
[ -z "$YTFZF_SUBT_NAME" ] && YTFZF_SUBT_NAME=""
#Stores the language for the auto genereated subtitles
selected_sub=${YTFZF_SELECTED_SUB-${selected_sub-}}


#> Clearing/Enabling fzf_defaults
#enable/disable the use of FZF_DEFAULT_OPTS
enable_fzf_default_opts=${YTFZF_ENABLE_FZF_DEFAULT_OPTS-${enable_fzf_default_opts-0}}
#clear FZF_DEFAULT_OPTS
[ "$enable_fzf_default_opts" -eq 0 ] && FZF_DEFAULT_OPTS=""

#> files and directories
history_file=${history_file-$cache_dir/ytfzf_hst}
search_history_file=${search_history_file-$cache_dir/ytfzf_search_hst}
current_file=${current_file-$cache_dir/ytfzf_cur}
thumb_dir=${thumb_dir-$cache_dir/thumb}
#> Stores urls of the video page of channels
subscriptions_file=${subscriptions_file-$config_dir/subscriptions}

#> stores the pid of running ytfzf sessions
pid_file="$cache_dir/.pid"
#> make folders that don't exist
[ -d "$cache_dir" ] || mkdir -p "$cache_dir"
[ -d "$thumb_dir" ] || mkdir -p "$thumb_dir"

#> config settings
#list of shortcuts to use in fzf
#handle_custom_shortcuts will be called if defined (handle_custom_shortcuts can call handle_shortcuts to handle normal shortcuts)
shortcuts="${shortcuts-alt-l,alt-t,alt-o,alt-v,alt-d,alt-m,alt-s,alt-enter}"
search_prompt=${search_prompt-Search: }
#used when getting the html from youtube

useragent=${useragent-'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.152 Safari/537.36'}
#used when fancy_subscriptions_menu is 1
fancy_subscriptions_text=${fancy_subscriptions_text-             -------%s-------}

#> config scripting settings
#whether or not to show errors when an invalid option is passed
exit_on_opt_error=${exit_on_opt_error-1}

#Opt variables (can also be set in config)
#use $YTFZF_EXT_MENU (same as -D)
is_ext_menu=${is_ext_menu-0}
#show thumbnails (same as -t)
show_thumbnails=${show_thumbnails-0}
# 1: better thumbnails (slower), 0: low res thumbnails (faster)
thumbnail_quality=${thumbnail_quality-1}
#auto select the first video (same as -a)
auto_select=${auto_select-0}
#select all results (same as -A)
select_all=${select_all-0}
#randomly select a video (same as -r)
random_select=${random_select-0}
#only show the selected link (same as -L)
show_link_only=${show_link_only-0}
#in case silent_mode is not enabled in config
[ $show_link_only -eq 1 ] && silent_mode=1
#weather or not to print non-error messages
silent_mode=${silent_mode-0}
#show different video formats (same as -f)
show_format=${show_format-0}
#number of links to select with -a or -r (same as -n)
link_count=${link_count-1}
#number of videos to show in the subscription menu
sub_link_count=${sub_link_count-10}
#after video ends, make another search (same as -s)
search_again=${search_again-0}
#whether or not to show -----------channel------------ when looking at subscriptions
fancy_subscriptions_menu=${fancy_subscriptions_menu-1}
#sort subscriptions subsections based on time instead of channel
sort_videos_data=${sort_videos_data-0}
#filter id used when searching
sp=${sp-}
#is used to know whether or not scraping the search page is necessary
scrape=${scrape-yt_search}
#auto generated caption by YouTube with enabled with --subt
auto_caption=${auto_caption-0}
#only play/download the audio if set to 1
is_audio_only=${is_audio_only-0}
#flag for downloading
is_download=${is_download-0}
#the tab for trending, empty is the default page
trending_tab="${trending_tab-}"
#the sort name, if exists will be called and it will set functions for sorting
sort_name="${sort_name-}"
#to detach the video player from the terminal
detach_player=${detach_player-0}
# misc
tab_space=$(printf '\t')
new_line="
"

#ueberzug related variables
#the side where thumbnails are shown
#needs to be exported because ueberzug spawns subprocesses
[ -z "$PREVIEW_SIDE" ] && export PREVIEW_SIDE=${preview_side-left}

#variable used for when this process spawns subprocesses and there needs to be a unique value (ueberzug)
#this could be any unique value, $$ is used because it is the most easily accessible unique value
if [ -z "$PROC_ID" ]; then
	export PROC_ID=$$
	printf "$$\n" >> "$pid_file"
fi

#dependency check
dep_ck () {
	for Dep; do
		if ! command -v "$Dep" 1>/dev/null; then
			printf "%s not found. Please install it.\n" "$Dep" >&2
			exit 2
		fi
	done
	unset Dep
}
dep_ck "jq" "youtube-dl" "curl"


#only check for mpv if $YTFZF_PLAYER is set to it
#don't check $YTFZF_PLAYER as it could be multiple commands
[ "$video_player" = "mpv" ] && dep_ck "mpv"

############################
#       Help Texts         #
############################

basic_helpinfo () {
	while IFS= read -r Line; do
		printf "%s\n" "$Line";
	done <<EOF
Usage: ytfzf [OPTIONS...] <search-query>;
  OPTIONS:
     -h, --help                             Show this help text;
     -v, --version                          -v for ytfzf's version;
                                            --version for ytfzf + dependency's versions
     -t, --show-thumbnails                  Show thumbnails (requires ueberzug)
                                            Doesn't work with -H -D
     --detach                               Detach the video player from the terminal
     --thumbnail-quality=<0,1>              0: low quality (faster), 1: default
     --thumb-disp-method=<method>           the program to use for displaying images,
                                            see --help-thumb-disp for a list of choices

     -N, --notification                     Send notification when playing video
     -D, --ext-menu                         Use external menu(default dmenu) instead of fzf
     -H, --choose-from-history              Choose from history
     -x, --clear-history=[watch|search]     Delete history
     -q, --search-history                   Open a fzf menu to choose query from search history
     -m, --audio-only                       Audio only (for music)
     -d, --download                         Download to current directory
     -f                                     Show available formats before proceeding
     -a, --auto-select                      Auto play the first result, no selector
     -r  --random-select                    Auto play a random result, no selector
     -A, --select-all                       Selects all results
     -n, --link-count=<link-count>          To specify number of videos to select with -a, -r
     -l, --loop                             Loop: prompt selector again after video ends
     -s, --search-again                     After the video ends make another search
     -L, --link-only                        Prints the selected URL only, helpful for scripting
     --silent                               Do not print anything to sdtout (errors will still be printed to stderr)
     -T, --trending=<gaming/music/movies>   The trending tab, using --trending= you can choose a specific tab
     -Y, --youtube                          Search youtube (default)
     -P, --peertube                         Search peertube
     --preview-side=<left/right/top/bottom> The side of the screen to show thumbnails
     --subt                                 Select auto-generated subtitles
     --sort                                 Sorts videos by latest upload date
     --sort-name=         <function>        The function must contain the values of
                                            data_sort_key, data_sort_fn,
					    see the example sort-name in the example config

  Use - instead of <search-query> for stdin

  Option usage:
     ytfzf -fDH                             to show history using external
                                            menu and show formats
     ytfzf -fD --choose-from-history        same as above

EOF
}

all_help_info () {
	basic_helpinfo
	printf "%s" "
  Subscriptions: to add a channel to subscptions, copy the channel's video page url
                 and add it to ~/.config/ytfzf/subscriptions. Each url must be on a new line
     -S,  --subs                          Get the latest 10 videos from subscriptions
     --subs=<number>                      Get the latest <number> of videos from subscriptions
     --fancy-subs=                        whether or not to show ------channel------ in subscriptions (must be 1 or 0)
     --add-subs				  generate a subscriptions file with subscriptions.json that you can get from google
                                          visit https://github.com/FreeTubeApp/FreeTube/wiki/Importing-Your-YouTube-Subscriptions for more info
  Filters: different ways to filter videos in search
     --upload-time=<time-range>           Time range can be one of,
                                          last-hour, today, this-week, this-month, this-year
                                          Filters can go directly: --today
     --upload-sort=<sort-filter>          The filter to sort the videos can be one of
                                          upload-date, view-count, rating
                                          Filters can go directly: --upload-date
     --filter-id=<filter>                 The id of the filter to use for video results
         A filter id can be found by going to Youtube searching, filtering how you want
         Then taking the value of the &sp= part of the url
         Filters may not work especially when the filter sorts for non-videos
         In addition this overrides any filter provided through options
         Example: ytfzf --filter-id=EgJAAQ minecraft
         This will filter by livestream

  Update:
     --update                             clones the latest stable commit and installs it
                                          on Arch ytfzf is available in the AUR
     --update-unstable                    gets the latest commit and installs it (--update is safer)


  Defaults can be modified through ENV variables or the config file
  the default config file can be found at https://github.com/pystardust/ytfzf/blob/master/docs/conf.sh

  Environment Variables:
     YTFZF_HIST=1                                       0 : off history
     YTFZF_NOTI=1                                       0 : turn off notification
     YTFZF_CACHE=~/.cache/ytfzf;
     YTFZF_CONFIG_DIR='~/.config/ytfzf'                 The directory to store config files
     YTFZF_CONFIG_FILE='\$YTFZF_CONFIG_DIR/conf.sh'     The configuration file
     YTFZF_LOOP=0                                       1 : loop the selection prompt
     YTFZF_PREF=''                                      22: 720p,  18: 360p (yt-dl formats)
     YTFZF_CUR=1                                        For status bar modules
     YTFZF_ENABLE_FZF_DEFAULT_OPTS=0                    1 : fzf will use FZF_DEFAULT_OPTS
     YTFZF_SELECTED_SUB=en                              Set default auto caption language (eg. English)
     YTFZF_EXTMENU=' dmenu -i -l 30'
  To use rofi: YTFZF_EXTMENU=' rofi -dmenu -fuzzy -width 1500'
"
}

thumbnail_display_help_info () {
    printf "%s" "
  There are a few different ways thumbnails can be displayed, most of which use ascii text to display images,
  options:

  ueberzug (default) only works with Xorg,

  catimg,

  jp2a (24 bit color),
  jp2a-8 (256 color palette),
  jp2a-4 (ansi colors),
  jp2a-grey/gray (greyscale),

  chafa (24 bit color)
  chafa-4 (ansi colors)
  chafa-8 (256 color palette)
  chafa-grey/gray (greyscale)

  custom use a custom way to show images
  handle_display_img must be defined in config for this to work
  handle_display_img will run the command you want to run for displaying images
  "
}

usageinfo () {
    printf "Usage: %bytfzf %b<search query>%b\n" "\033[1;32m" "\033[1;33m" "\033[0m";
    printf "     'ytfzf -h' for more information\n";
}

print_info () {
    [ $show_link_only -eq 1 ] || [ $silent_mode -eq 1 ] && return
    printf "$*"
}

print_error () {
    [ $ext_menu_notifs -eq 1 ] && notify-send "error" "$*" || printf "\033[31m$*\033[0m" >&2
    [ $ext_menu_notifs -eq 1 ] && notify-send "Check for new versions and report at: https://github.com/pystardust/ytfzf\n" || printf "Check for new versions and report at: https://github.com/pystardust/ytfzf\n" >&2
}

############################
#        Formatting        #
############################
#> Colors  (printf)
c_red="\033[1;31m"
c_green="\033[1;32m"
c_yellow="\033[1;33m"
c_blue="\033[1;34m"
c_magenta="\033[1;35m"
c_cyan="\033[1;36m"
c_reset="\033[0m"


#> To determine the length of each field (title, channel ... etc)
format_ext_menu () {
	#base how much space everything takes up depending on the width of YTFZF_EXT_MENU
	frac=$(((external_menu_len - 5 - 12)/11))
	#title space
	title_len=$((frac * 6 - 1))
	#channel space
	channel_len=$((frac * 3/2))
	#video duration space
	dur_len=$((frac * 1))
	#video view space
	view_len=$((frac * 1))
	#video upload date space
	date_len=$((frac * 3/2 + 100 ))
	url_len=100
}
format_fzf () {
	dur_len=7
	view_len=10
	date_len=14
	url_len=100

	#*_len works the same as it does in format_ext_menu
	#show title, channel
	if [ "$TTY_COLS" -lt 75 ]; then
		frac=$(((TTY_COLS - 1)/4))
		title_len=$((frac * 3))
		channel_len=$((frac * 1 + 7))
	#show title, channel, time
	elif [ "$TTY_COLS" -lt 95 ]; then
		frac=$(((TTY_COLS - 4)/8))
		title_len=$((frac * 5 - 1))
		channel_len=$((frac * 2 - 1))
		dur_len=$((frac * 1 + 10))
	#show title, channel, time, views
	elif [ "$TTY_COLS" -lt 110 ]; then
		frac=$(((TTY_COLS - 1)/9))
		title_len=$((frac * 5 ))
		channel_len=$((frac * 2 ))
		dur_len=$((frac * 1))
		view_len=$((frac * 1 + 7))
	#show title, channel, time, views, date
	else
		frac=$((TTY_COLS/10 ))
		title_len=$((frac * 5 - 1))
		channel_len=$((frac * 2))
		dur_len=$((frac * 1 - 5 ))
		view_len=$((frac * 1))
		date_len=$((frac * 2 + 20))
	fi
}
#> Formats the fields depending on which menu is needed. And assigns the menu command.
format_menu () {
	if [ "$is_ext_menu" -eq 0 ]; then
		#dep_ck fzf here because it is only necessary to use here
		dep_ck "fzf"
		menu_command='column -t -s "$tab_space" | fzf -m --bind change:top --tabstop=1 --layout=reverse --delimiter="$tab_space" --nth=1,2 --expect="$shortcuts" $FZF_DEFAULT_OPTS'
		format_fzf
	else
		# Dmenu doesn't render tabs so removing it
		menu_command='tr -d "$tab_space" | '"$external_menu"
		format_ext_menu
	fi
}

function_exists () {
	type "$1" > /dev/null 2>&1 && return 0 || return 1
}

# video_info_text can be set in the conf.sh, if set it will be preferred over the default given below
if ! function_exists 'video_info_text'; then
	video_info_text () {
		printf "%-${title_len}.${title_len}s\t" "$title"
		printf "%-${channel_len}.${channel_len}s\t" "$channel"
		printf "%-${dur_len}.${dur_len}s\t" "$duration"
		printf "%-${view_len}.${view_len}s\t" "$views"
		printf "%-${date_len}.${date_len}s\t" "$date"
		printf "%s" "$shorturl"
		printf "\n"
	}
fi

format_video_data () {
	while IFS=$tab_space read -r title channel views duration date shorturl; do
	    video_info_text
	done << EOF
$*
EOF
	unset title channel duration views date shorturl
}

############################
#       Image previews     #
############################

if ! function_exists 'thumbnail_video_info_text' ; then
    thumbnail_video_info_text () {
	printf "\n ${c_cyan}%s" "$title"
	printf "\n ${c_blue}Channel	${c_green}%s" "$channel"
	printf "\n ${c_blue}Duration	${c_yellow}%s" "$duration"
	printf "\n ${c_blue}Views	${c_magenta}%s" "$views"
	printf "\n ${c_blue}Date	${c_cyan}%s" "$date"
	printf "\n ${c_blue}Description ${c_reset}: %s" "$description"
    }
fi

## The following snippet of code has been copied and modified from
# https://github.com/OliverLew/fontpreview-ueberzug      MIT License
# Ueberzug related variables

#the is doesn't have to be the $$ it just has to be unique for each instance of the script
#$$ is the easiest unique value to access that I could think of

FIFO="/tmp/ytfzf-ueberzug-fifo-$PROC_ID"
ID="ytfzf-ueberzug"
WIDTH=$FZF_PREVIEW_COLUMNS
HEIGHT=$FZF_PREVIEW_LINES
start_ueberzug () {
    [ -e $FIFO ] || { mkfifo "$FIFO" || exit 1 ; }
    ueberzug layer --parser json --silent < "$FIFO" &
    exec 3>"$FIFO"
}
stop_ueberzug () {
    exec 3>&-
    rm "$FIFO" > /dev/null 2>&1
}

preview_img () {

	args="$*"
	shorturl=${args##*${tab_space}|}
	shorturl="${shorturl%% *}"

	json_obj=$(jq '.[]|select( .videoID == "'"$shorturl"'")' "$tmp_video_json_file")

	IFS=$tab_space read -r title channel duration views date description <<-EOF
	$(
		printf "%s" "$json_obj" | jq -r \
		'
		[.title,.channel,.duration,.views,.date,.description]|@tsv
		'
	)
	EOF

	# if the entry is a channel separator then clear the image
	if [ -z "${shorturl%% *}" ] ; then
		printf "\n${c_cyan}%s${c_reset}\n" "$title"
		printf '{ "action": "remove", "identifier": "%s" }\n' "$ID" > "$FIFO"
		return
	fi

	# Out put the formatted text on the (left)panel
	thumbnail_video_info_text

	thumb_width=$((WIDTH - 2 ))
	thumb_height=$((HEIGHT - 2))
	#most common x, y positions

	thumb_x=$((TTY_COLS / 2 + 3))
	thumb_y=10

	case $PREVIEW_SIDE in
	    left)
		thumb_x=1
		;;
	    top)
		thumb_height=$((HEIGHT - 5))
		thumb_y=2
		;;
	    bottom)
		thumb_height=$((HEIGHT - 5))
		thumb_y=$((TTY_LINES / 2 + 3))
		;;
	esac

	IMAGE="$thumb_dir/${shorturl%% *}.png"
	case "$YTFZF_THUMB_DISP_METHOD" in
	    ueberzug)
		[ ! -p "$FIFO" ] && exit
		# In fzf the cols and lines are those of the preview pane
		{   printf '{ "action": "add", "identifier": "%s", "path": "%s",' "$ID" "$IMAGE"
		    printf '"x": %d, "y": %d, "scaler": "fit_contain",' $thumb_x $thumb_y
		    printf '"width": %d, "height": %d }\n' "$thumb_width" "$thumb_height"
		} > "$FIFO" ;;
	    catimg)
		printf "\n"
		catimg -w "$((thumb_width * 2))" "$IMAGE" ;;
	    jp2a)
		printf "\n"
		jp2a --size="${thumb_width}x$((thumb_height / 2))" --colors  --color-depth=24 "$IMAGE" ;;
	    jp2a-8)
		printf "\n"
		jp2a --size="${thumb_width}x$((thumb_height / 2))" --colors  --color-depth=8 "$IMAGE" ;;
	    jp2a-4)
		printf "\n"
		jp2a --size="${thumb_width}x$((thumb_height / 2))" --colors  --color-depth=4 "$IMAGE" ;;
	    jp2a-gray|jp2a-grey)
		printf "\n"
		jp2a --size="${thumb_width}x$((thumb_height / 2))" "$IMAGE" ;;
	    chafa)
		printf "\n"
		chafa --size="${thumb_width}x${thumb_height}" "$IMAGE" ;;
	    chafa-gray|chafa-grey)
		printf "\n"
		chafa --size="${thumb_width}x${thumb_height}" --colors=2 "$IMAGE" ;;
	    chafa-4)
		printf "\n"
		chafa --size="${thumb_width}x${thumb_height}" --colors=16 "$IMAGE" ;;
	    chafa-8)
		printf "\n"
		chafa --size="${thumb_width}x${thumb_height}" --colors=256 "$IMAGE" ;;
	    custom)
		if ! function_exists "handle_display_img"; then
		    printf "\033[031mERROR[#07]: \033[0m\033[1mhandle_display_img\033[0m is not defined" >&2
		    exit 3
		fi
		handle_display_img "$thumb_width" "$thumb_height" "$thumb_x" "$thumb_y" "$IMAGE" ;;
	esac
	unset title channel duration views date shorturl
	unset thumb_width thumb_height thumb_x thumb_y IMAGE
	unset args
}

############################
#   Video selection Menu   #
############################
video_menu () {
	#take input format it to the appropriate format, then pipe it into the menu
	format_video_data "$*" | eval "$menu_command"
}


############################
#         Scraping         #
############################

download_thumbnails () {
       #scrapes the urls of the thumbnails of the videos from the adjusted json
	if [ "$thumbnail_quality" -eq 1 ]; then
		image_download () {
			# higher quality images
			curl -s "$Url" -G --data-urlencode "sqp=" > "$thumb_dir/$Name.png"
		}
	else
		image_download () {
 			curl -s "$Url"  > "$thumb_dir/$Name.png"
		}
	fi

	print_info "Downloading Thumbnails...\n"
	thumb_urls=$(printf "%s" "$*" |\
		jq  -r '.[]|[.thumbs,.videoID]|@tsv' )

	while IFS=$tab_space read -r Url Name; do
	    sleep 0.001
		{
			image_download
		} &
	done <<-EOF
	$thumb_urls
	EOF
	unset Name Url thumb_urls
	unset -f image_download
}

get_sp_filter () {

	#filter_id is a variable that keeps changing throught this function
	filter_id=

	#sp is the final filter id that is used in the search query
	sp=

	#the way youtube uses these has a pattern, for example
	    #in the sort_by_filter the only difference is the 3rd character, I just don't know how to use this information efficiently
	case $sort_by_filter in
		upload-date) filter_id="CAISBAgAEAE" ;;
		view-count) filter_id="CAMSBAgAEAE" ;;
		rating) filter_id="CAESBAgAEAE" ;;
	esac

	#another example is sort by filter + upload date filter only changes one character as well
	if [ -n "$filter_id" ]; then
		#gets the character in the filter_id that needs to be replaced if upload_date_filter is also given
		upload_date_character=$(printf "%s" "$filter_id" | awk '{print substr($1, 8, 1)}')
	fi

	#For each of these, if upload_date_character is unset, the filter_id should be the normal filter
	#Otherwise set the upload_date_character to the right upload_date_character
	case $upload_date_filter in
		last-hour)
			[ -z "$upload_date_character" ] && filter_id="EgQIARAB" || upload_date_character="B" ;;
		today)
			[ -z "$upload_date_character" ] && filter_id="EgQIAhAB" || upload_date_character="C" ;;
		this-week)
			[ -z "$upload_date_character" ] && filter_id="EgQIAxAB" || upload_date_character="D" ;;
		this-month)
			[ -z "$upload_date_character" ] && filter_id="EgQIBBAB" || upload_date_character="E" ;;
		this-year)
			[ -z "$upload_date_character" ] && filter_id="EgQIBRAB" || upload_date_character="F" ;;
	esac

	#if upload_date_character isn't empty, set sp to upload_date filter + sort_by filter
	if [ -n "$upload_date_character" ]; then
		#replaces the 8th character in the filter_id with the appropriate character
		#the 8th character specifies the upload_date_filter
		sp=$(printf "%s" "$filter_id" | sed 's/\(.\{7\}\)./\1'"$upload_date_character"'/')
	#otherwise set it to the filter_id
	else
		sp=$filter_id
	fi
	unset upload_date_character filter_id
}

get_yt_json () {
	# scrapes the json embedded in the youtube html page
	printf "%s" "$*" | sed -n '/var *ytInitialData/,$p' | tr -d '\n' |\
        sed -E ' s_^.*var ytInitialData ?=__ ; s_;</script>.*__ ;'
}

get_yt_html () {
    link=$1
    query=$2
    printf "%s" "$(
	curl "$link" -s \
	  -G --data-urlencode "search_query=$query" \
	  -G --data-urlencode "sp=$sp" \
	  -H 'Authority: www.youtube.com' \
	  -H "User-Agent: $useragent" \
	  -H 'Accept-Language: en-US,en;q=0.9' \
	  -L \
	  --compressed
    )"
    unset link query
}

get_video_data () {
	# outputs tab and pipe separated fields: title, channel, view count, video length, video upload date, and the video id/url
	# from the videos_json
	printf "%s" "$*" |\
	    jq -r '.[]| "\(.title)'"$tab_space"'|\(.channel)'"$tab_space"'|\(.views)'"$tab_space"'|\(.duration)'"$tab_space"'|\(.date)'"$tab_space"'|\(.videoID)"'
}

scrape_channel () {
	# needs channel url as $*
	## Scrape data and store video information in videos_data ( and thumbnails )

	channel_url=$*

	# Converting channel title page url to channel video url
	if ! printf "%s" "$channel_url" | grep -q '/videos *$'; then
		channel_url=${channel_url%/featured}/videos
	fi

	yt_html=$(get_yt_html "$channel_url")

	if [ -z "$yt_html" ]; then
	        print_error "ERROR[#01]: Couldn't curl website. Please check your network and try again.\n"
	        exit 1
	fi

	#gets the channel name from title of page
	channel_name=$(printf "%s" "$yt_html" | grep -o '<title>.*</title>' |
		sed \
		-e 's/ - YouTube//' \
		-e 's/<\/\?title>//g' \
		-e "s/&apos;/'/g" \
		-e "s/&#39;/'/g" \
		-e "s/&quot;/\"/g" \
		-e "s/&#34;/\"/g" \
		-e "s/&amp;/\&/g" \
		-e "s/&#38;/\&/g"
		)

	#gets json of videos
	yt_json=$(get_yt_json "$yt_html")

	#gets a list of videos
	videos_json=$(printf "%s" "$yt_json" |\
	jq '[ .contents | ..|.gridVideoRenderer? |
	select(. !=null) |
	    {
	    	title: .title.runs[0].text,
	    	channel:"'"$channel_name"'",
	    	duration:.thumbnailOverlays[0].thumbnailOverlayTimeStatusRenderer.text.simpleText,
	    	views: .shortViewCountText.simpleText,
	    	date: .publishedTimeText.simpleText,
	    	videoID: .videoId,
	    	thumbs: .thumbnail.thumbnails[0].url,
	    }
	]')

	videos_json=$(printf "%s" "$videos_json" | jq '.[0:'$sub_link_count']')
	printf "%s\n" "$videos_json" >> "$tmp_video_json_file"
	#checks if it's empty in case it was defined in a config function eg: on_get_search
	[ -z "$videos_data" ] && videos_data=$(get_video_data "$videos_json")
	function_exists "on_video_data_gotten" && on_video_data_gotten "$videos_data" "$videos_json" "$yt_json"

	#if there aren't videos
	if [ -z "$videos_data" ]; then
	    print_error "No results found. Make sure the link ($channel_url) is correct.\n(if this chnanel has not uploaded videos this warning may appear)\n"
	    exit 1
	fi
	if [ $fancy_subscriptions_menu -eq 1 ]; then
		printf "$fancy_subscriptions_text\t\n%s\n" "$channel_name" "$videos_data" >> "$tmp_video_data_file"
	else
		printf "%s\n" "$videos_data" >> "$tmp_video_data_file"
	fi

	[ $show_thumbnails -eq 1 ] && download_thumbnails "$videos_json"
	unset channel_url channel_name yt_html yt_json videos_json
}

get_trending_url_data () {
    case "$trending_tab" in
	music) printf "%s" "4gINGgt5dG1hX2NoYXJ0cw%3D%3D" ;;
	gaming) printf "%s" "4gIcGhpnYW1pbmdfY29ycHVzX21vc3RfcG9wdWxhcg%3D%3D" ;;
	movies) printf "%s" "4gIKGgh0cmFpbGVycw%3D%3D" ;;
    esac
}

scrape_pt () {
     #gets a list of videos
     pt_json=$(
     curl \
         -s "https://sepiasearch.org/api/v1/search/videos" \
	 -G --data-urlencode "search=$*") 
     videos_json=$(printf "%s" "$pt_json" |\
	jq '[ .data | .[] |
	    {
		title: .name,
		channel: .channel.displayName,
		duration: "\(.duration / 60 | floor):\(.duration % 60)",
		views: .views,
		date: .publishedAt,
		videoID: .uuid,
		url: .url,
		thumbs: .thumbnailUrl
	    }
	]')
     printf "%s" "$videos_json" > "$tmp_video_json_file"
     videos_data="$(get_video_data "$videos_json")"
     function_exists "on_video_data_gotten" && on_video_data_gotten "$videos_data" "$videos_json" "$pt_json"
     [ $show_thumbnails -eq 1 ] && download_thumbnails "$videos_json"
     unset pt_json
}

scrape_yt () {
	# needs search_query as $*
	## Scrape data and store video information in videos_data ( and thumbnails )

	#sp is the urlquery youtube uses for sorting videos
	#only runs if --filter-id or --sp was unspecified
	if [ -z "$sp" ]; then
		get_sp_filter
	else
		#youtube puts in %253d one ore more times in the filter id, it doesn't seem useful, so we are removing it if it's in the filter
		sp=${sp%%%*}
	fi

	print_info "Scraping Youtube...\n"

	# scrape trending or search query provided
	if [ "$scrape" = "trending" ]; then
	    tab_data="$(get_trending_url_data)"
	    [ -z "$tab_data" ] && [ -n "$trending_tab" ] && printf "\033[31mERROR[#05]: \033[1m\$trending_tab\033[0m\033[31m must be one of:\n\033[2;39mmusic, gaming, movies\033[0m\n\033[31myou put: \033[1m\"$trending_tab\"\033[0m\n" >&2 && exit 3
	    yt_html=$(get_yt_html "https://www.youtube.com/feed/trending?bp=$tab_data")
	else
	    yt_html=$(get_yt_html "https://www.youtube.com/results" "$*")
	fi

	if [ -z "$yt_html" ]; then
		print_error "ERROR[#01]: Couldn't curl website. Please check your network and try again.\n"
		exit 1
	fi

	yt_json=$(get_yt_json "$yt_html")

	#if the data couldn't be found
	if [ -z "$yt_json" ]; then
		print_error "ERROR[#02]: Couldn't find data on site.\n"
		exit 1
	fi

	#gets a list of videos
	videos_json=$(printf "%s" "$yt_json" | jq '[ .contents|
	..|.videoRenderer? |
	select(. !=null) |
		{
			title: .title.runs[0].text,
			channel: .longBylineText.runs[0].text,
			duration:.lengthText.simpleText,
			views: .shortViewCountText.simpleText,
			date: .publishedTimeText.simpleText,
			description: .detailedMetadataSnippets[0].snippetText.runs[0].text,
			videoID: .videoId,
			thumbs: .thumbnail.thumbnails[0].url
		}
	]')

	playlist_json=$(printf "%s" "$yt_json" | jq '[ .contents|
	..|.playlistRenderer? |
	select(. !=null) |
		{
			title: "[Playlist] \(.title.simpleText)",
			channel: .longBylineText.runs[0].text,
			duration: "\(.videoCount) videos",
			views: "playlist",
			date: "playlist",
			videoID: .playlistId,
			thumbs: .thumbnails[0].thumbnails[0].url
		}
	]')

	videos_json=$playlist_json$videos_json
	printf "%s" "$videos_json" > "$tmp_video_json_file"

	#checks if it's empty in case it was defined in a config function eg: on_get_search
	[ -z "$videos_data" ] && videos_data=$(get_video_data "$videos_json")
	#if there aren't videos
	if [ -z "$videos_data" ]; then
	    printf "No results found. Try different keywords.\n" >&2
	    exit 1
	fi
	function_exists "on_video_data_gotten" && on_video_data_gotten "$videos_data" "$videos_json" "$yt_json"

	[ "$sort_videos_data" -eq 1 ] && videos_data=$(printf "%s" "$videos_data" | sort_video_data_fn)

	[ $show_thumbnails -eq 1 ] && download_thumbnails "$videos_json"
	# wait for thumbnails to download
	wait
	unset playlist_json yt_json yt_html
}


############################
#      User selection      #
############################
#> To get search query
get_search_query () {
	#in case no query was provided
	if [ -z "$search_query" ]; then
        if [ $enable_search_hist_menu -eq 1 ]; then
            search_history_menu
		elif [ "$is_ext_menu" -eq 1 ]; then
			#when using an external menu, the query will be done there
			search_query=$( : | eval "$external_menu" )
		else
			#otherwise use the search prompt
			printf "$search_prompt"
			read -r search_query
		fi
		[ -z "$search_query" ] && clean_up && exit 0
	fi
    set_search_history
	function_exists "on_get_search" && on_get_search "$search_query"
}
#> To select videos from videos_data
user_selection () {
	#remove subscription separators
	videos_data_clean=$(printf "%s" "$videos_data" | sed "/.*$tab_space$/d")

	#$selected_data is the video the user picked
	#picks the first n videos
	if [ "$select_all" -eq 1 ] ; then
		selected_data=$videos_data_clean
	elif [ "$auto_select" -eq 1 ] ; then
		selected_data=$(printf "%s\n" "$videos_data_clean" | sed "${link_count}"q )
	#picks n random videos
	elif [ "$random_select" -eq 1 ] ; then
	    selected_data=$(printf "%s\n" "$videos_data_clean" | posix_shuf | head -n${link_count}) 
	    #posix_shuf, pick the first $link_count videos

	#show thumbnail menu
	elif [ "$show_thumbnails" -eq 1 ] ; then
		dep_ck "ueberzug" "fzf"
		export YTFZF_THUMB_DISP_METHOD="$thumb_disp_method"
		[ "$thumb_disp_method" = "ueberzug" ] && start_ueberzug
		#thumbnails only work in fzf, use fzf
		menu_command="fzf -m --tabstop=1 --bind change:top --delimiter=\"$tab_space\" \
		--nth=1,2 --expect='$shortcuts' $FZF_DEFAULT_OPTS \
		--layout=reverse --preview \"sh $0 -U {}\" \
        	--preview-window \"$PREVIEW_SIDE:50%:noborder:wrap\""
		selected_data=$( title_len=200 video_menu "$videos_data" )
		[ "$thumb_disp_method" = "ueberzug" ] && stop_ueberzug
		# Deletes thumbnails if no video is selected
		[ -z "$selected_data" ] && clean_up
	#show regular menu
	else
		selected_data=$( video_menu "$videos_data" )
	fi
	unset videos_data_clean
}

handle_shortcuts () {
    [ -z "$selected_key" ] && return 0
    #creates splits the variable shortcuts by , and assigns a variable to each string
    #ei: $urls_shortcut will be equal to the first keybind
    IFS="," read -r \
	urls_shortcut title_shortcut open_browser_shortcut watch_shortcut \
	download_shortcut audio_shortcut search_shortcut detach_shortcut _ <<-EOF
	$shortcuts
	EOF

    case $selected_key in
	"$urls_shortcut") printf "%s\n" $selected_urls; return 1 ;;
	"$title_shortcut") 
	    printf "%s\n" "$selected_data" | awk -F "  " '{print $1}'; return 1 ;;
	"$open_browser_shortcut")
	    for url in $selected_urls; do
		nohup $BROWSER "$url" >/dev/null 2>&1
	    done
	    return 1 ;;
	"$watch_shortcut") is_download=0; is_audio_only=0; return 0;;
	"$download_shortcut") is_download=1; return 0;;
	"$audio_shortcut") is_audio_only=1; return 0;;
	"$detach_shortcut") detach_player=1; return 0;;
	"$search_shortcut")
	    unset videos_data search_query
	    [ "$scrape" = "pt_search" ] && scrape_fn || scrape="yt_search" scrape_fn
	    return 2 ;;
    esac
    if function_exists "handle_custom_shortcuts"; then
	handle_custom_shortcuts
	return $?
    fi
    return 0
}

format_user_selection () {
	#for each url append the full url to the $urls string
	#through this loop, the selected data which was truncated by formatting is retrived.
	selected_key="${selected_data%%${new_line}*}"
	selected_urls=
	while read data_line; do
		surl=${data_line##*|}
		[ -z "$surl" ] && continue # to ignore the separators
		#literally just check if there is :// to make sure it's a url (just in case the url doesn't use https or http)
		#peertube urls can differ a lot
		case ${#surl} in
					# youtube video
			11) selected_urls=$selected_urls$new_line'https://www.youtube.com/watch?v='$surl ;;
			34) selected_urls=$selected_urls$new_line'https://www.youtube.com/playlist?list='$surl ;;
			36)
			    selected_urls=$selected_urls$new_line"$(printf "%s" "$videos_json" | jq '.[].url' | grep -F "$surl" | tr -d '"')" ;;
			*) continue ;;
		esac
		refined_selected_data=$refined_selected_data$new_line$(printf '%s' "$videos_data" | grep "|$surl" )
	done<<-EOF
	$selected_data
	EOF

	selected_urls=${selected_urls#$new_line}
	[ -z "$selected_urls" ] && exit # exit if no valid url

	#sometimes % shows up in selected data, could throw an error if it's an invalid directive
	selected_data=${refined_selected_data#$new_line}
	unset shorturls refined_selected_data
}

print_data () {
	if [ $show_link_only -eq 1 ] ; then
		printf "%s\n" "$selected_urls"
		exit
	fi
}

get_video_format () {
	# select format if flag given
	[ $show_format -eq 0 ] && return
        formats=$(youtube-dl -F "$(printf "$selected_urls")") 
        line_number=$(printf "$formats" | grep -n '.*extension  resolution.*' | cut -d: -f1)
        quality=$(printf "$formats \n1 2 xAudio" | awk -v lineno=$line_number 'FNR > lineno {print $3}' | sort -n |  awk -F"x" '{print $2 "p"}' | uniq | sed -e "s/Audiop/Audio/" -e "/^p$/d" | eval "$menu_command" | sed "s/p//g")
		[ -z "$quality"  ] && exit;
		[ $quality = "Audio"  ] && video_pref= && video_player="$audio_player" || video_pref="bestvideo[height=?$quality][vcodec!=?vp9]+bestaudio/best"

	unset max_quality quality
}

get_sub_lang () {
    if [ $auto_caption -eq 1 ]; then
        #Gets the auto generated subs and stores them in a file
        sub_list=$(youtube-dl --list-subs  --write-auto-sub "$selected_urls" | sed '/Available subtitles/,$d' | awk '{print $1}' | sed '1d;2d;3d')
        if [ -n "$sub_list" ]; then
            [ -n "$selected_sub" ] ||  selected_sub=$(printf "$sub_list" | eval "$menu_command") &&  youtube-dl  --sub-lang $selected_sub  --write-auto-sub --skip-download "$selected_urls" -o /tmp/ytfzf && YTFZF_SUBT_NAME="--sub-file=/tmp/ytfzf.$selected_sub.vtt" || printf "Auto generated subs not available."
        fi
	unset sub_list
    fi

}


open_player () {

	if [ $is_audio_only -eq 1 ]; then
		video_player=$audio_player
		video_pref="bestaudio"
	fi

	if [ $detach_player -eq 1 ]; then
		if [ -z "$video_pref" ] || [ $is_audio_only -eq 1 ]; then
			printf "Opening Player: %s\n" "$video_player \"$*\""
			setsid -f $video_player "$@"  $YTFZF_SUBT_NAME >/dev/null 2>&1
		else
			printf "Opening Player: %s\n" "$video_player_format$video_pref \"$*\""
			setsid -f $video_player_format"$video_pref"  "$@"  $YTFZF_SUBT_NAME >/dev/null 2>&1
		fi
		return
	fi

	if [ $is_download -eq 0 ]; then
		if [ -z "$video_pref" ] || [ $is_audio_only -eq 1 ]; then
			printf "Opening Player: %s\n" "$video_player \"$*\""
			$video_player "$@"  $YTFZF_SUBT_NAME
		else
			printf "Opening Player: %s\n" "$video_player_format$video_pref \"$*\""
			$video_player_format"$video_pref"  "$@"  $YTFZF_SUBT_NAME || [ $? -eq 4 ] || video_pref= open_player "$@"
		fi
	elif [ $is_download -eq 1 ]; then
		if [ -z "$video_pref" ]; then
			youtube-dl "$@"  "$YTFZF_SUBT_NAME"
      dl_status=`[ $? -eq 0 ] && echo Successful || echo Failed`
		else
			youtube-dl -f "$video_pref"  "$@"  $YTFZF_SUBT_NAME || video_pref= open_player "$@"
      dl_status=`[ $? -eq 0 ] && echo Successful || echo Failed`
		fi

    notify-send "[ytfzf] Download $dl_status!" "$*"
	fi
}

play_url () {
	#> output the current track to current file before playing

	[ "$enable_cur" -eq 1 ] && printf "%s" "$selected_data" > "$current_file" ;

	[ "$enable_noti" -eq 1 ] && send_notify "$selected_data" ;

	# word splitting on the urls below, -f to remove globbing
	set -f
	if function_exists "handle_urls"; then
	    handle_urls $selected_urls
	else
	    open_player $selected_urls
	fi

	#Delete the temp auto-gen subtitle file
	[ $auto_caption -eq 1 ] && rm -f "${YTFZF_SUBT_NAME#*=}"

	unset player_urls
}
#> Checks if sessions are running
session_is_running () {
    session_count=0
    while read -r pid; do
	[ -d /proc/"$pid" ] && session_count=$(( session_count + 1 ))
    done < "$pid_file"
    [ $session_count -eq 1 ] && return 1 || return 0
}
#> removes tmp files and clutter
clean_up () {
	if ! session_is_running ; then
		[ -d "$thumb_dir" ] && rm -r "$thumb_dir"
		: > "$pid_file"
		function_exists "on_exit" && on_exit
	fi
	#clean up should never fail, it may fail in user's on_exit, but should be handled by on_exit
	return 0
}
#> Saves data before exiting
save_before_exit () {
	[ $is_url -eq 1 ] && exit
	[ $enable_hist -eq 1 ] && printf "%s\n" "$selected_data" >> "$history_file" ;
	[ $enable_cur -eq 1 ] && : > "$current_file" ;
}


############################
#         Misc             #
############################
#> if the input is a url then skip video selection and play the url
check_if_url () {
	# to check if given input is a url
	url_regex='^https\?://.*'
	if printf "%s" "$1" | grep -q "$url_regex"; then
		is_url=1
		selected_urls=$(printf "%s" "$1" | tr ' ' '\n')
		scrape="url"
	else
		is_url=0
	fi
	unset url_regex
}

#> Loads history in videos_data
get_history () {
	if [ "$enable_hist" -eq 1 ]; then
		[ -e "$history_file" ] || : > "$history_file"
		#gets history data in reverse order (makes it most recent to least recent)
		hist_data=$( sed '1!G; h; $!d' "$history_file" )
		[ -z "$hist_data" ] && printf "History is empty!\n" >&2 && return 1;
		#removes duplicate values from $history_data
		videos_data=$(printf "%s" "$hist_data" | uniq )
		[ "$sort_videos_data" -eq 1 ] && videos_data="$(printf "%s" "$videos_data"  | sort_video_data_fn)"
	else
		printf "History is not enabled. Please enable it to use this option (-H).\n" >&2;
		exit 1;
	fi
	unset hist_data
}

clear_history () {
    case "$1" in
        search) h_file="${search_history_file}" ;;
        watch) h_file="${history_file}" ;;
        *) h_file="${search_history_file}|${history_file}" ;;
    esac

    exit_code=0
    IFS="|"
    for file in $h_file; do
	[ "$file" = "$history_file" ] && name="History" || name="Search History"
        if [ -e "$file" ]; then
            : > "$file"
            printf "%s has been cleared\n" "$name"
        else
            printf "\033[31m%s file not found, history not cleared\033[0m\n" "$name" >&2
            exit_code=1
        fi
    done
    exit $exit_code
}

get_search_history () {
	if [ "$enable_search_hist" -eq 1 ]; then
		[ -e "$search_history_file" ] || : > "$search_history_file"
		#gets history data in reverse order (makes it most recent to least recent)
		hist_data=$( sed '1!G; h; $!d' "$search_history_file" )
		[ -z "$hist_data" ] && printf "Search history is empty!\n" >&2 && return 1;
		#removes duplicate values from $history_data
		search_history=$(printf "%s" "$hist_data" | uniq )
	else
		printf "Search history is not enabled. Please enable it to use this option (-q).\n" >&2;
		exit 1;
	fi
	unset hist_data
}

set_search_history () {
    [ -z "$search_query" ] && return
    [ $enable_search_hist -eq 1 ] && printf "%s\t%s\n" "$(date '+%Y-%m-%d %H:%M:%S')" "$search_query" >> "$search_history_file" ;
}

search_history_menu () {
    ! get_search_history && [ "$allow_empty_search_hist" -eq 0 ] && exit 1

    if [ "$is_ext_menu" -eq 1 ]; then
    #when using an external menu, the search history will be done there
        choice=$( printf "%s\n" "$search_history" | eval "$external_menu" )
    else
        choice="$( printf "%s\n" "$search_history" | fzf --prompt="$search_history_prompt" --print-query --no-multi -d '\t' --with-nth=2.. --expect='alt-enter' --bind='tab:replace-query' )"
    fi

    # first line is the fzf query (what the user types in fzf)
    # second line is the fzf --expect key pressed
    # third line is the search_history selection made
    query="$( printf "%s" "$choice" | sed -n '1p' )"
    key="$( printf "%s" "$choice" | sed -n '2p' )"
    selection="$( printf "%s" "$choice" | sed -n '3p' )"

    # if no search history selection has been made
    # and the user typed a query, use that instead
    if [ -z "$selection" ]; then
        # exit if no selection or query
        [ -z "$query" ] && exit
        search_query="$query"
        return
    fi

    case $key in
        alt-enter) # Always use query
            [ -z "$query" ] && exit
            search_query="$query"
            return;;
    esac
    search_query="$( printf "%s" "$selection" | awk -F'\t' '{printf "%s", $NF}' )"
}

! function_exists "send_select_video_notif" && send_select_video_notif () {
	#message=$title\nchannel: $channel
	#${var#|} removes the extra | at the start of the text
	message="${video_title#|}\nChannel: ${video_channel#|}"
	video_thumb="$config_dir/default_thumb.png"

	#if more than 1 video selected
	if [ $videos_selected_count -gt 1 ]; then
		message="Added $videos_selected_count video to play queue"
	#if show thumbnails and videos_selected is 1 (it will never be less than 1)
	elif [ "$show_thumbnails" -eq 1 ]; then
		video_thumb="$thumb_dir/${video_shorturl#|}.png"
	fi

	#if downloading, say Downloading not currently playing
	[ $is_download -eq 1 ] && title="Downloading" || title="Currently playing"
	notify-send "$title" "$message" -i "$video_thumb"

	unset message video_thumb title
}

send_notify () {
	videos_selected_count=$(printf "%s\n" "$*" | wc -l)
	while IFS=$tab_space read -r video_title video_channel video_views video_duration video_date video_shorturl; do
	    send_select_video_notif
	done << EOF
$*
EOF
	unset videos_selected_count video_title video_channel video_views video_duration video_date video_shorturl
}

update_ytfzf () {
	branch="$1"
	updatefile="$(mktemp)"
	curl -L "https://raw.githubusercontent.com/pystardust/ytfzf/$branch/ytfzf" -o "$updatefile"

	if sed -n '1p' < "$updatefile" | grep -q '#!/bin/sh'; then
		chmod 755 "$updatefile"
		function_exists "sudo" && doasroot="sudo" || doasroot="doas"
		mv -f "$updatefile" "$0" 2>/dev/null || $doasroot mv -f "$updatefile" "$0"
		if [ $? -eq 0 ] ; then
			unset doasroot
			printf "%bytfzf successfully updated.%b\n" "$c_green" "$c_reset"
			exit 0
		else
			unset doasroot
			printf "%bFailed to update ytfzf.%b\n" "$c_red" "$c_reset"
			rm "$updatefile"
			exit 1
		fi
	else
		printf "%bFailed to update ytfzf. Try again later.%b\n" "$c_red" "$c_reset"
		rm "$updatefile"
		exit 1
	fi
}

#gives a value to sort by (this will give the unix time the video was uploaded)
if ! function_exists "data_sort_key"; then
    data_sort_key () {
	sort_by="${5#|}"
	sort_by="${sort_by#Streamed}"
	#print the data that should be sorted by
	printf "%d" "$(date -d "${sort_by}" '+%s')"
	unset sort_by
    }
fi
#the function to use for sorting
if ! function_exists "data_sort_fn"; then
    data_sort_fn () {
	sort -nr
    }
fi
sort_video_data_fn () {
	while IFS= read -r line
	do
		IFS="$tab_space"
		#run the key function to get the value to sort by
		printf "%s\t%s\n" "$(data_sort_key $line)" "$line"
	done | data_sort_fn | cut -f2-
	unset IFS line 
}

scrape_subscriptions () {
	# clear the subfile, all subscriptions data will be dumped into this file
	: > "$tmp_video_data_file"
	: > "$tmp_video_json_file"

	[ $sort_videos_data -eq 1 ] && fancy_subscriptions_menu=0

	while IFS= read -r url; do
		scrape_channel "$url" &
	done <<-EOF
	$( sed \
	-e "s/#.*//" \
	-e "/^[[:space:]]*$/d" \
	-e "s/[[:space:]]*//g" \
	"$subscriptions_file")
	EOF
	wait
	if [ $sort_videos_data -eq 1 ]; then 
		videos_data=$(sort_video_data_fn < "$tmp_video_data_file")
	else
		videos_data=$(cat "$tmp_video_data_file")
	fi
}

#create subscriptions file from data downloaded from youtube
create_subs () {

    yt_sub_import_file="${config_dir}/subscriptions.json"

    # there is a decent guide on getting this data by the FreeTube devs
    if [ ! -f "$yt_sub_import_file" ]; then
        printf "\033[31mYou need to have a subscriptions.json file in the ${config_dir} directory!\033[0m\n"
        printf "For further information visit https://github.com/FreeTubeApp/FreeTube/wiki/Importing-Your-YouTube-Subscriptions !\n"
        exit
    fi

    # start fresh
    : > "$config_dir/subscriptions"

    # check how many subscriptions there are in the file
    sublength=$( jq '. | length' < "$yt_sub_import_file" )

    for i in $(seq $((sublength - 1))); do
        channelInfo=$(jq --argjson index ${i} '[ "https://www.youtube.com/channel/" + .[$index].snippet.resourceId.channelId + "/videos", "#" + .[$index].snippet.title ]' < "$yt_sub_import_file")
	printf "%s\n" "$(printf "%s" "$channelInfo" | tr -d '[]"\n,')" >> "$subscriptions_file"
    done
    exit
}

verify_thumb_disp_method () {
    #make sure the thumb_disp_method is a valid method
    case "$thumb_disp_method" in
	ueberzug|jp2a-grey|jp2a-gray|jp2a|jp2a-4|jp2a-8|catimg|custom|chafa|chafa-grey|chafa-gray|chafa-4|chafa-8) ;;
	*)
	    print_error "ERROR[#06]: $thumb_disp_method is not a valid display method\nrun ytfzf --help-thumb-disp for more"
	    exit 3 ;;
    esac
}

#sort -R is not posix
posix_shuf () {
    awk -F '\n' '
	BEGIN {srand()} #set the random seed at the start
	{print rand() " " $0} #prepend a random number for each line' |\
    sort | sed -E 's/[^ ]* //'
    #sort by the random numbers, remove the random number
}

is_non_number () {
	[ "$1" -eq "$1" ] 2>/dev/null && return 1 || return 0
}

bad_opt_arg () {
	arg=${1:-"$optarg"}
	opt=${2:-"--$opt="}
	printf "%s\n" "$opt requires a numeric arg, but was given \"$arg\"" >&2
	exit 3
}

parse_opt () {
	#the first arg is the option
	opt=$1
	#second arg is the optarg
	optarg=$2
	case ${opt} in
		h|help)	basic_helpinfo
			printf "type --help-all for more info\n"
			exit ;;
		help-all)
			all_help_info
			exit ;;

		help-thumb-disp)
			thumbnail_display_help_info
			exit ;;

		D|is-ext-menu)
			is_ext_menu=${optarg:-1}
			is_non_number "$is_ext_menu" && bad_opt_arg ;;

		m|audio-only)
			is_audio_only=${optarg:-1}
			is_non_number "$is_audio_only" && bad_opt_arg ;;

		d|download)
			is_download=${optarg:-1} ;;

		f) 	show_format=1 ;;

		H|choose-from-history)
			scrape="history" ;;

		x|clear-history)
			clear_history "${optarg}" && exit ;;
		q|search-history) 
			enable_search_hist_menu=${optarg:-1}
			is_non_number "$enable_search_hist_menu" && bad_opt_arg ;;
		a|auto-select)
			auto_select=${optarg:-1}
			is_non_number "$auto_select" && bad_opt_arg ;;

		A|select-all)
			select_all=${optarg:-1}
			is_non_number "$select_all" && bad_opt_arg ;;

		r|random-select)
			random_select=${optarg:-1}
			is_non_number "$random_select" && bad_opt_arg ;;

		s|search-again)
			search_again=${optarg:-1}
			is_non_number "$search_again" && bad_opt_arg ;;

		P|peer-tube)
			scrape="pt_search" ;;
		Y|youtube)
			scrape="yt_search" ;;
		sort)
			sort_videos_data=${optarg:-1}
			is_non_number "$sort_videos_data" && bad_opt_arg ;;
		sort-name)
			sort_videos_data=1
			sort_name="$optarg" ;;

		S)	
			scrape="yt_subs" ;;
		subs)
			sub_link_count=$optarg
			is_non_number "$sub_link_count" && bad_opt_arg
			parse_opt "S" ;;

		fancy-subs)
			fancy_subscriptions_menu=${optarg:-1}
			is_non_number "$fancy_subscriptions_menu" && bad_opt_arg ;;
			
		T|trending)	
			trending_tab=${optarg:-}
			scrape="trending" ;;

		l|loop)
			enable_loop=${optarg:-1}
			is_non_number "$enable_loop" && bad_opt_arg ;;

		t|show-thumbnails)
			show_thumbnails=${optarg:-1}
			is_non_number "$show_thumbnails" && bad_opt_arg ;;

		thumb-disp-method)
			thumb_disp_method="$optarg"
			verify_thumb_disp_method ;;

		thumbnail-quality)
			thumbnail_quality=$optarg
			is_non_number "$thumbnail_quality" && bad_opt_arg
			parse_opt "t" ;;

		v)	printf "ytfzf: %s\n" "$YTFZF_VERSION"
			exit ;;
		version)
			printf "\033[1mytfzf:\033[0m %s\n" "$YTFZF_VERSION"
			printf "\033[1myoutube-dl:\033[0m %s\n" "$(youtube-dl --version)"
			command -v "fzf" 1>/dev/null && printf "\033[1mfzf:\033[0m %s\n" "$(fzf --version)"
			exit ;;

		subt)
			auto_caption=${optarg:-1}
			is_non_number "$auto_caption" && bad_opt_arg ;;

		L|link-only)
			show_link_only=${optarg:-1}
			is_non_number "$show_link_only" && bad_opt_arg ;;

		silent)
			silent_mode=${optarg:-1}
			is_non_number "$silent_mode" && bad_opt_arg ;;

		n|link-count)
			link_count="$optarg"
			is_non_number "$link_count" && bad_opt_arg "$link_count" "-n" ;;

		U) 	preview_img "$optarg"; exit;
			# This option is reserved for the script, to show image previews
			# Not to be used explicitly
			;;

		N|notification)
			enable_noti=${optarg:-1}
			is_non_number "$enable_noti" && bad_opt_arg ;;

		add-subs) create_subs ;;

		upload-time) upload_date_filter=$optarg ;;
		last-hour|today|this-week|this-month|this-year) upload_date_filter=$opt ;;

		upload-sort) sort_by_filter=$optarg ;;
		upload-date|view-count|rating) sort_by_filter=$opt ;;

		filter-id|sp) sp=$optarg ;;

		preview-side) export PREVIEW_SIDE=$optarg ;;

		detach) 
			detach_player=${optarg:-1}
			is_non_number "$detach_player" && bad_opt_arg ;;

		update) update_ytfzf "master" ;;
		update-unstable) update_ytfzf "${optarg:-development}" ;;

		*)
			if [ "$exit_on_opt_error" -eq 1 ]; then
			    usageinfo >&2
			    exit 2
			fi ;;
	esac
	unset opt optarg
}


while getopts "LhDmdfxqHaArltSsvNTPYn:U:-:" OPT; do
	# to parse extra-options in conf.sh
	# when there is no = in OPTARG and it's a longopt, OPTARG will = OPT
	if [ "$OPT" = "-" ]; then
		OPT=${OPTARG%%=*}
		OPTARG=${OPTARG#*=}
		[ "$OPTARG" = "$OPT" ] && OPTARG=""
	fi
	# to parse extra-options in conf.sh ( and don't parse during thumbnail call)
	function_exists "on_opt_parse" && on_opt_parse "$OPT" "$OPTARG"
	parse_opt "$OPT" "$OPTARG"
done
shift $((OPTIND-1))

#only apply to ext_menu since they dont have a terminal to print to
[ $is_ext_menu -eq 1 ] && command -v notify-send 1>/dev/null 2>&1 && ext_menu_notifs=1 || ext_menu_notifs=0

#used for thumbnail previews in ueberzug
if [ $is_ext_menu -eq 0 ]; then
	export TTY_LINES=$(tput lines)
 	export TTY_COLS=$(tput cols)
fi

#if both are true, it defaults to using fzf, and if fzf isnt installed it will throw an error
#so print this error instead and set $show_thumbnails to 0
if [ $is_ext_menu -eq 1 ] && [ $show_thumbnails -eq 1 ]; then
	[ $ext_menu_notifs -eq 1 ] &&\
	    notify-send "warning" "Currently thumbnails do not work in external menus" ||\
	    printf "\033[33mWARNING: Currently thumbnails do not work in external menus\033[0m\n" >&2
	show_thumbnails=0
fi

#is_url is 0 by default
is_url=0

#if stdin is given and no input (including -) is given, throw error
#also make sure its not reading from ext_menu
if [ ! -t 0 ] && [ -z "$*" ] && [ $is_ext_menu -eq 0 ]; then
	print_error "ERROR[#04]: Use - when reading from stdin\n"
	exit 2
#read stdin if given
elif [ "$*" = "-" ]; then
	print_info "Reading from stdin\n"
	while read -r line
	do
	    search_query="$search_query $line"
	done
elif [ -n "$*" ]; then
    check_if_url "${search_query:=$*}"
fi

# If in auto select mode dont download thumbnails
[ $auto_select -eq 1 ] || [ $random_select -eq 1 ] && show_thumbnails=0;

#format the menu screen
format_menu

#if sort_name is define it will define data_sort_key and data_sort_fn
#otherwise nothing will happen
$sort_name

scrape_fn () {
    case $scrape in
	"trending")
		scrape_yt ;;
	"yt_search")
		get_search_query
		scrape_yt "$search_query" ;;
	"pt_search")
		get_search_query
		scrape_pt "$search_query" ;;
	"yt_subs")
		scrape_subscriptions
		;;
	"history")
		! get_history && exit 1
		;;
	"url")
	    get_video_format
	    play_url
	    exit
	    ;;
	*)
	    printf "\033[31mError: \$scrape set to bad option, set to '$scrape'${c_reset}\n"
	    exit 1 ;;

    esac
}
scrape_fn

while true; do
	user_selection
	#to renable it for a new search
	format_user_selection
	#if handle_shortcuts returns 1, exit
	handle_shortcuts
	case "$?" in
	    1)
		save_before_exit
		clean_up
		exit ;;
	    2) continue ;;
	esac
	print_data
	get_video_format
	get_sub_lang
	play_url
	save_before_exit

	#if looping and searching_again arent on then exit
	if [ $enable_loop -eq 0 ] && [ $search_again -eq 0 ] ; then
		clean_up
		exit
	fi

	#if -s was specified make another search query
	if [ $search_again -eq 1 ] && [ $enable_loop -eq 0 ] ; then
		unset videos_data search_query
		scrape_fn
		continue
	fi
done
