aboutsummaryrefslogblamecommitdiffstats
path: root/ytfzf
blob: 7d9876aea1d77bef4cf67559e1e46459101016ab (plain) (tree)
1
2
3
4
5
6
7
8
9
         
 


                                      
 


                            
 


                                    
                                                         
                                     
                                                                    
                                                    
 


                                                                         
                                 
                                                                          
                                                               
 
                        

                                     
                              
                                
                                            
                                        



                                   
                                       
                                
                                 



                                                           
  
 


                                                 





                                                                                                            

                                           

 


                            
             

                                                                                           





                                                                                                      

                                                                                   
                                                                                              

                                                                                             
                                                                                                         

                                                                                                  
                                                                                                         
                                                       
           
                          


                                                                                    










                                                                                    
                                                                                        









                                                                                    





                                                                 





                                                 
                                       

                  





                   
                                                             



                                                                                           
                                   



                                             
                                     




                                             
                                      





                                          
            






                                               

          
                                                                                      


                                       
                                                                                                                                             




                                             

                                                         
                                                                        

                               
 
                                                                   
               
                                       
                                                                                                     
                                                                                                                
 





                            
               
                                              
 
                   
                     
                                                                                                                         


                                                          




                            
                                                                  
                                                                    







                                   

                                                   


                                                     









                                                                                        
                                                                    

                                                                             

                                                                                              
 





                            


                                                              
 














                                                                                                                 
 
 


                                                                                            
                       
                   


                                                                                                                                         
                                                 



                                                
          
                                  
                                                                                                                       


                       
 
                                                                                            

                                                                  
           
                                    
                                                                                   


                       
 

                                                                                           
                                                                                                                                                                      
                                                                                   
                  
                                                                                                  
 
                                                         
 















                                                                            
                                                       






                                                              
                   
                                   
 
                    
 
                                        
                                                                              
                                            
                                                                             
                                              
                                 
                              
                                                                  
                              
            
                                                              
          
        




                                                                               
                                          
                                                                   



                                                                                             

                                                             
 
                                           
                            

                    
 
                                     
                                        
                                                                                                
                                                                   
                                            
                

             
                                                                  
                                                                           


                                                                   
                                 
                                                                                                                      
              
                                                                                                               
                                                                 
              
                                                                                                                                                                                                
         
 










                                                                               
                 




                                          

                         



                        














                                                                                             








                 
            

    
              
                                    
              

                                  
                      

                             
                                                                                                              
                                           














                                                                     
 



                                                            
                                                                              






                                                                  




                                                                       
                               
                              
                                





                                                 
                                                 
                                                               
 



                                        
                                                                                                                    
                               
 



                                          
                                      









                                               

                                                                                        
                                                   
                          
 
                  
                                 
                                 
            



                                           

                   
 

                                          
                             





                                                                         

                                 
  
 


                      
                        
        
                                      
                                      


                                                      
                               

                                         
          
    
#!/bin/sh

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

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

[ -z "$YTFZF_HIST" ] && YTFZF_HIST=1
[ -z "$YTFZF_LOOP" ] && YTFZF_LOOP=0
[ -z "$YTFZF_CUR" ] && YTFZF_CUR=1
[ -z "$YTFZF_CACHE" ] && YTFZF_CACHE="$HOME/.cache/ytfzf"
[ -z "$YTFZF_PREF" ] && YTFZF_PREF=""
[ -z "$YTFZF_EXTMENU" ] && YTFZF_EXTMENU='dmenu -i -l 30 -p Search:'
[ -z "$YTFZF_EXTMENU_LEN" ] && YTFZF_EXTMENU_LEN=220

## player settings
[ -z "$YTFZF_PLAYER" ] && YTFZF_PLAYER="mpv"
[ -z "$YTFZF_PLAYER_FORMAT" ] && YTFZF_PLAYER_FORMAT="mpv --ytdl-format="
#> Clearing/Enabling fzf_defaults
[ -z "$YTFZF_ENABLE_FZF_DEFAULT_OPTS" ] && YTFZF_ENABLE_FZF_DEFAULT_OPTS=0
[ $YTFZF_ENABLE_FZF_DEFAULT_OPTS -eq 0 ] && FZF_DEFAULT_OPTS=""

#> files and directories
history_file="$YTFZF_CACHE"/ytfzf_hst
current_file="$YTFZF_CACHE"/ytfzf_cur
thumb_dir="$YTFZF_CACHE"/thumb
#> make folders that don't exist
[ -d $YTFZF_CACHE ] || mkdir -p $YTFZF_CACHE
[ -d $thumb_dir ] || mkdir -p $thumb_dir
## For MacOS users
if [ "$(uname)" = "OpenBSD" ]; then
	alias sed='\gsed'
fi
#> Setting and reading the config file 
config_dir="$HOME/.config/ytfzf"
config_file="$config_dir/conf.sh"
if [ -e $config_file ]
then
    #source config file if exists (overrides env variables)
    . "$config_file"
fi

#> settings only set in config
search_prompt="${search_prompt-Search Youtube: }"

# DEP CHECK
dep_ck () {
	for dep in "$@"; do
		command -v "$dep" 1>/dev/null || { printf "$dep not found. Please install it.\n" ; exit 2; }
	done
}
dep_ck "jq" "youtube-dl";
[ "$YTFZF_PLAYER" = "mpv" ] && dep_ck "mpv"


############################
#       Help Texts         #
############################
helpinfo () {
printf "Usage: %bytfzf [OPTIONS] %b<search-query>%b\n" "\033[1;32m" "\033[1;33m" "\033[0m";
printf "  OPTIONS:\n"
printf "     -h, --help                           Show this help text\n";
printf "     -t, --thumbnails                     Show thumbnails (requires ueberzug)\n";
printf "                                          Doesn't work with -H -D\n";
printf "     -D, --ext-menu                       Use external menu(default dmenu) instead of fzf \n";
printf "     -H, --choose-from-history            Choose from history \n";
printf "     -x, --clear-history                  Delete history\n";
printf "     -m, --audio-only   <search-query>    Audio only (for music)\n";
printf "     -d, --download     <search-query>    Download to current directory\n";
printf "     -f                 <search-query>    Show available formats before proceeding\n";
printf "     -a, --auto-play    <search-query>    Auto play the first result, no selector\n";
printf "     -r  --random-play  <search-query>    Auto play a random result, no selector\n";
printf "     -n, --video-count= <video-count>     To specify number of videos to select with -a or -r\n";
printf "     -l, --loop         <search-query>    Loop: prompt selector again after video ends\n";
printf "     -s                 <search-query>    After the video ends make another search \n";
printf "     -L, --link-only    <search-query>    Prints the selected URL only, helpful for scripting\n";
printf "  Use - instead of <search-query> for stdin\n";
printf "\n"
printf "  Option usage:\n"
printf "     ytfzf -fDH                           to show history using external \n"
printf "                                          menu and show formats\n"
printf "     ytfzf -fD --choose-from-history      same as above\n"
printf "\n"
printf "  Defaults can be modified through ENV variables\n";
printf "  Defaults:\n";
printf "     YTFZF_HIST=1                          0 : off history\n";
printf "     YTFZF_CACHE=~/.cache/ytfzf\n";
printf "     YTFZF_LOOP=0                          1 : loop the selection prompt\n";
printf "     YTFZF_PREF=''                         22: 720p,  18: 360p\n";
printf "     YTFZF_CUR=1                           For status bar bodules\n";
printf "     YTFZF_EXTMENU=' dmenu -i -l 30'\n";
printf "  To use rofi\n";
printf "     YTFZF_EXTMENU=' rofi -dmenu -fuzzy -width 1500'\n";
printf "     YTFZF_ENABLE_FZF_DEFAULT_OPTS=0       1 : fzf will use FZF_DEFAULT_OPTS\n";
printf "\n";
printf "  For more details refer https://github.com/pystardust/ytfzf\n";
}
usageinfo () {
printf "Usage: %bytfzf %b<search query>%b\n" "\033[1;32m" "\033[1;33m" "\033[0m";
printf "     'ytfzf -h' for more information\n";
}
errinfo () {
printf "Check for new versions and report at: https://github.com/pystardust/ytfzf\n"
}


############################
#        Formatting        #
############################
#> To determine the length of each field (title, channel ... etc)
format_ext_menu () {
	frac=$(((YTFZF_EXTMENU_LEN - 5 - 12)/11))
	title_len=$((frac * 6 - 1))
	channel_len=$((frac * 3/2))
	dur_len=$((frac * 1))
	view_len=$((frac * 1))
	date_len=$((frac * 3/2 + 100 ))
	url_len=12
}
format_fzf () {
	dur_len=7
	view_len=10
	date_len=14
	url_len=12

	#> Get the terminal size to adjust length accordingly
	t_size="$(stty size 2> /dev/null | cut -f2 -d' ')"
	if [ "$t_size" = "" ]; then
	    printf "\e[31mWhen using stdin put - for the search query\033[000m\n" && exit 2
	fi
	if [ $t_size -lt 75 ]; then
		# title channel
		frac=$(((t_size - 1)/4))
		title_len=$((frac * 3))
		channel_len=$((frac * 1 + 7))
	elif [ $t_size -lt 95 ]; then
		# title channel time
		frac=$(((t_size - 4)/8))
		title_len=$((frac * 5 - 1))
		channel_len=$((frac * 2 - 1))
		dur_len=$((frac * 1 + 10))
	elif [ $t_size -lt 110 ]; then
		# title channel time views
		frac=$(((t_size - 1)/9))
		title_len=$((frac * 5 ))
		channel_len=$((frac * 2 ))
		dur_len=$((frac * 1))
		view_len=$((frac * 1 + 7))
	else
		# title channel time views date
		frac=$(((t_size - 5)/11))
		title_len=$((frac * 5 - 1))
		channel_len=$((frac * 2))
		dur_len=$((frac * 1))
		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"
		menu_command='fzf -m --bind change:top --tabstop=1 --layout=reverse --delimiter="$(printf "\t")" --nth=1,2 $FZF_DEFAULT_OPTS'
		if [ $is_stdin -eq 0 ] ; then
			format_fzf 
		else 
			format_ext_menu
		fi
	else
		# dmenu doesnt render tabs so removing it
		menu_command='tr -d "$(printf "\t")" | '"$YTFZF_EXTMENU"
		format_ext_menu
	fi
}
#> Trucating and formatting the fields based on the decided lengths
format_awk () {
	printf "%s" "$*" | awk -F'\t' \
	-v A=$title_len -v B=$channel_len -v C=$dur_len -v D=$view_len -v E=$date_len -v F=$url_len \
	'{ printf "%-"A"."A"s\t%-"B"."B"s\t%-"C"."C"s\t%-"D"."D"s\t%-"E"."E"s\t%-"F"."F"s\n",$1,$2,$4,$3,$5,$6}'
}



############################
#   Video selection Menu   #
############################
video_menu () {
	format_awk "$*" | eval "$menu_command"
}
video_menu_img () {
	title_len=400
	format_awk "$*" | fzf -m --tabstop=1 --bind change:top --delimiter="$(printf "\t")" --nth=1,2 $FZF_DEFAULT_OPTS \
		--layout=reverse --preview "sh $0 -U {}" \
        	--preview-window "left:50%:noborder:wrap"
}


############################
#       Image previews     #
############################
## The following snippet of code has been copied and modified from
# https://github.com/OliverLew/fontpreview-ueberzug      MIT License
# Ueberzug related variables
FIFO="/tmp/ytfzf-ueberzug-fifo"
IMAGE="/tmp/ytfzf-ueberzug-img.png"
ID="ytfzf-ueberzug"
WIDTH=$FZF_PREVIEW_COLUMNS
HEIGHT=$FZF_PREVIEW_LINES

start_ueberzug () {
    [ -e $FIFO ] || { mkfifo "$FIFO" || exit 1 ; }
    [ -e $IMAGE ] || { touch "$IMAGE" || exit 1 ; }
    ueberzug layer --parser json --silent < "$FIFO" &
    exec 3>"$FIFO"
}
stop_ueberzug () {
    exec 3>&-
    rm "$FIFO" "$IMAGE" > /dev/null 2>&1
}

preview_img () {
	shorturl="$(echo "$@" | sed -E -e 's_.*\|([^|]+) *$_\1_')"
	# In fzf the cols and lines are those of the preview pane
	IMAGE="$thumb_dir/$shorturl.png"
	{   printf '{ "action": "add", "identifier": "%s", "path": "%s",' "$ID" "$IMAGE"
	    printf '"x": %d, "y": %d, "scaler": "fit_contain",' 2 10
	    printf '"width": %d, "height": %d }\n' "$WIDTH" "$((HEIGHT - 2))"
	} > "$FIFO" 
	echo "$@" |  tr -d '|' | sed "s/ *\t/\t/g" | awk -F'\t' \
	'{ printf "\n%s\nChannel: %s\nViews: %s\nDuration: %s\nUploaded %s\n",$1,$2,$4,$3,$5}'
}


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

download_thumbnails () {
	thumb_urls="$(printf "%s" "$videos_json" |\
		jq  '.thumbnail.thumbnails[0].url,.videoId' )"

	[ $show_link_only -eq 0 ] && printf "Downloading Thumbnails.."
    
	rm -r $thumb_dir/* 1>/dev/null 2>&1
	i=0
	while read line; do
		[ $((i % 2)) -eq 0 ] && { url="$(echo $line | sed -E 's/^"//;s/\.png.*/\.png/')" ;}
		[ $((i % 2)) -eq 1 ] && {
			name="$(echo "$line" | tr -d '"')"
			{ curl -s "$url" > "$thumb_dir/$name.png" && [ $show_link_only -eq 0 ] && printf "."; } &
		}
		i=$((i + 1))
	done << EOF
$thumb_urls
EOF
	wait && [ $show_link_only -eq 0 ] && echo ""

}
scrape_yt () {
	# needs search_query
	## Scrape data and store video information in videos_data ( and thumbnails download)
	## GETTING DATA
	yt_html="$(
	useragent='user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.152 Safari/537.36'

	curl 'https://www.youtube.com/results' -s \
	  -G --data-urlencode "search_query=$*" \
	  -H 'authority: www.youtube.com' \
	  -H "$useragent" \
	  -H 'accept-language: en-US,en;q=0.9' \
	  --compressed
	)"
	if [ -z "$yt_html" ]; then
		printf "\033[31mERROR[#01]: Couldn't curl website. Please check your network and try again.\033[000m\n"
		errinfo
		exit 2
	fi

	yt_json="$(printf "%s" "$yt_html" | sed -n '/var *ytInitialData/,$p' | tr -d '\n' |\
        sed -E ' s_^.*var ytInitialData ?=__ ; s_;</script>.*__ ;'

	 )"
	if [ -z "$yt_json" ]; then  
		printf "\033[31mERROR[#02]: Couldn't find data on site.\033[000m\n"
		errinfo
		exit 2
	fi

	videos_json="$(printf "%s" "$yt_json" | jq '..|.videoRenderer?' | sed '/^null$/d')"
	videos_data="$( printf "%s" "$videos_json" |\
		jq  '.title.runs[0].text,.longBylineText.runs[0].text,.shortViewCountText.simpleText,.lengthText.simpleText,.publishedTimeText.simpleText,.videoId' |\
		sed 's/^"//;s/"$//;s/\\"//g' | sed -E -n ";N;N;N;N;N;s/\n/\t\|/g;p"
		)"
	[ -z "$videos_data" ] &&  { printf "No results found. Try different keywords.\n"; exit 1;}

	[ $show_thumbnails -eq 1 ] && download_thumbnails
}


############################
#      User selection      #
############################
#> To get search query
get_search_query () {
	if [ $is_stdin -eq 1 ]; then
		while read line; do
			search_query="$search_query $line"
		done
	else
		if [ -z "$search_query" ]; then
			if [ $is_ext_menu -eq 1 ]; then 
				search_query="$(printf "" | $YTFZF_EXTMENU)"
			else
				printf "$search_prompt"
				read search_query
			fi
			[ -z "$search_query" ] && {  exit 0; }
		fi
	fi
}
#> To select videos from videos_data
user_selection () {
	[ $is_url -eq 1 ] && return

	format_menu 

	if [ $auto_select -eq 1 ] ; then
		selected_data="$(echo "$videos_data" | sed ${link_count}q )" ;
	elif [ $random_select -eq 1 ] ; then
		selected_data="$(echo "$videos_data" | shuf -n $link_count )"
	elif [ $show_thumbnails -eq 1 ] ; then
		dep_ck "ueberzug"
		start_ueberzug
		selected_data="$( video_menu_img "$videos_data" )"
		stop_ueberzug 
	else
		selected_data="$( video_menu "$videos_data" )"
	fi
	
	shorturls="$(echo "$selected_data" | sed -E -e 's_.*\|([^|]+) *$_\1_')"
	[ -z "$shorturls" ] && exit;
	urls=""
	selected_data=""
	while read surl; do
		[ -z "$surl" ] && continue
		urls="$urls\nhttps://www.youtube.com/watch?v=$surl"
		selected_data="$selected_data\n$(echo "$videos_data" | grep -m1 -e "$surl" )"
	done<<EOF
	$shorturls
EOF
	urls="$( printf "$urls" | sed 1d )"
	selected_data="$( printf "$selected_data" | sed 1d )"

	if [ $show_link_only -eq 1 ] ; then
		echo "$urls"
		exit
	fi

	# select format if flag given
	if [ $show_format -eq 1 ]; then 
		YTFZF_PREF="$(youtube-dl -F "$(printf "$urls" | sed 1q)" | sed '1,3d' | tac - |\
		eval "$menu_command" | sed -E 's/^([^ ]*) .*/\1/')"
		[ -z $YTFZF_PREF  ] && exit;
	fi	
}
play_url () {
	#> output the current track to current file before playing
	[ $YTFZF_CUR -eq 1 ] && printf "$selected_data" > "$current_file" ;

	#> Play url with $player or $player_format based on options
	#> if player format fails, then use normal player
	[ -n "$YTFZF_PREF" ] && {
		eval "$YTFZF_PLAYER_FORMAT"\'$YTFZF_PREF\'  "\"$(printf "$urls" | tr '\n' '\t' | sed 's_\t_" "_g' )\""
	} || {
		[ 4 -eq $? ] || eval "$YTFZF_PLAYER" "\"$(printf "$urls" | tr '\n' '\t' | sed 's_\t_" "_g' )\""
                     # Ctr-C in MPV results in a return code of 4
	} || {
		printf "\033[31mERROR[#03]: Couldn't play the video/audio using the current player: ${YTFZF_PLAYER}\n\t\033[000mTry updating youtube-dl\n"; errinfo ; save_before_exit ; exit 2;
	}
}
save_before_exit () {
	[ $is_url -eq 1 ] && exit
	[ $YTFZF_HIST -eq 1 ] && printf "$selected_data\n" >> "$history_file" ;
	[ $YTFZF_CUR -eq 1 ] && printf "" > "$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
	regex='^https\?://.*'
	echo "$1" | grep -q "$regex"
	if [ $? -eq 0 ] ; then
		is_url=1
		urls="$1"
		scrape=0
	else
		is_url=0
	fi
}
#> Loads history in videos_data
get_history () {
	if [ $YTFZF_HIST -eq 1 ]; then
		[ -e "$history_file" ] || touch "$history_file"
		hist_data="$(tac "$history_file")"
		[ -z "$hist_data" ] && printf "History is empty!\n" && exit;
		videos_data="$(echo "$hist_data" | uniq )"
		scrape=0
	else
		printf "History is not enabled. Please enable it to use this option (-H).\n";
		exit;
	fi
}


# Opt variables
is_ext_menu=0
show_thumbnails=0
show_link_only=0
scrape=1
auto_select=0
random_select=0
search_again=0
show_format=0
link_count=1

#OPT
parse_opt () {
	#the first arg is the option
	opt=$1
	#second arg is the optarg 
	OPTARG="$2"
	case ${opt} in
		#Long options
		-)
			#if the option has a short version it calls this function with the opt as the shortopt
			case "${OPTARG}" in
			        help) parse_opt "h" ;;

				ext-menu) parse_opt "D" ;;

				download) parse_opt "d" ;;

				choose-from-history) parse_opt "H" ;;

				clear-history) parse_opt "x" ;;

				random-select) parase_opt "r" ;;

				search) parse_opt "s" ;;

				loop) parse_opt "l" ;;

				thumbnails) parse_opt "t" ;;

				link-only) parse_opt "L" ;;

				video-count=*) parse_opt "n" "${OPTARG#*=}" ;;

				audio-only) parse_opt "m" ;;

				auto-play) parse_opt "a" ;;

				random-auto-play) parse_opt "r" ;;

				*)
				    printf "Illegal option --$OPTARG\n"
				    usageinfo
				    exit 2;
				    ;;
			esac ;;
		#Short options
		h) 	helpinfo
			exit ;;

		D) 	is_ext_menu="1" ;;

		m) 	YTFZF_PREF="bestaudio" ;;

		d) 	YTFZF_PLAYER="youtube-dl"
			YTFZF_PLAYER_FORMAT="youtube-dl -f " ;;

		f) 	show_format=1 ;;

		H)	get_history ;;

		x)	[ -e "$history_file" ] && (echo "" > "$history_file") && printf "History has been cleared\n"
			exit ;;

		a) 	auto_select=1 ;;

		r)	random_select=1 ;;

		s)	search_again=1
		 	YTFZF_LOOP=1 ;;

		l) 	YTFZF_LOOP=1 ;;

		t) 	show_thumbnails=1 ;;

		L) 	show_link_only=1 ;;

		n)	link_count="$OPTARG" ;;

		U) 	[ -p "$FIFO" ] && preview_img "$OPTARG"; exit;
			# This option is reserved for the script, to show image previews
			# Not to be used explicitly
			;;

		*)
			usageinfo
			exit 2 ;;
	esac
}

while getopts "LhDmdfxHarltsn:U:-:" opt; do
    parse_opt "$opt" "$OPTARG"
done
shift $((OPTIND-1))

[ "$*" = "-" ] && is_stdin=1 || is_stdin=0
search_query="$*"
check_if_url "$search_query" 
# If in auto select mode don't download thumbnails
[ $auto_select -eq 1 ] || [ $random_select -eq 1 ] && show_thumbnails=0; 


#call scrape function
if [ $scrape -eq 1 ]; then 
	get_search_query
	scrape_yt "$search_query"
fi

while true; do
	user_selection
	play_url
	save_before_exit
	
	#if looping isn't on then exit
	[ $YTFZF_LOOP -eq 0 ] && exit;

	#if -s was specified make another search query
	if [ $search_again -eq 1 ]; then
		search_query=""
		get_search_query
		scrape_yt "$search_query"
	fi
done
Un proyecto texto-plano.xyz