mirror of
				https://gitea.invidious.io/iv-org/youtube-utils.git
				synced 2024-08-15 00:53:16 +00:00 
			
		
		
		
	Merge pull request #1 from iv-org/add-yt-api-bash-utility
Add YT API bash utility
This commit is contained in:
		
						commit
						3244003608
					
				
					 2 changed files with 424 additions and 0 deletions
				
			
		
							
								
								
									
										14
									
								
								.github/workflows/shellcheck.yml
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								.github/workflows/shellcheck.yml
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | ||||||
|  | name: shellcheck-validation | ||||||
|  | 
 | ||||||
|  | on: push | ||||||
|  | 
 | ||||||
|  | jobs: | ||||||
|  |   shellcheck: | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  | 
 | ||||||
|  |     steps: | ||||||
|  |       - name: Clone repository | ||||||
|  |         uses: actions/checkout@v2 | ||||||
|  | 
 | ||||||
|  |       - name: Execute ShellCheck | ||||||
|  |         run: shellcheck scripts/*.sh | ||||||
							
								
								
									
										410
									
								
								scripts/yt-api-helper.sh
									
										
									
									
									
										Executable file
									
								
							
							
						
						
									
										410
									
								
								scripts/yt-api-helper.sh
									
										
									
									
									
										Executable file
									
								
							|  | @ -0,0 +1,410 @@ | ||||||
|  | #!/bin/sh | ||||||
|  | # shellcheck disable=SC2236 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | print_help() | ||||||
|  | { | ||||||
|  | 	echo "Usage: yt-api-helper  -i [-c <client>] [-e <endpoint>]" | ||||||
|  | 	echo "Usage: yt-api-helper  -e <endpoint> -d <data>" | ||||||
|  | 	echo "" | ||||||
|  | 	echo "Options:" | ||||||
|  | 	echo "  -c,--client       Client to use. Pass 'help' to this option to get" | ||||||
|  | 	echo "                      the list of supported clients" | ||||||
|  | 	echo "  -d,--data         Raw data to send to the API" | ||||||
|  | 	echo "  -e,--endpoint     Youtube endpoint to request. Pass 'help' to this" | ||||||
|  | 	echo "                      option to get the list of supported endpoints" | ||||||
|  | 	echo "  -h,--help         Show this help" | ||||||
|  | 	echo "  -i,--interactive  Run in interactive mode" | ||||||
|  | 	echo "  -o,--output       Print output to file instead of stdout" | ||||||
|  | 	echo "" | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | print_clients() | ||||||
|  | { | ||||||
|  | 	echo "Available clients:" | ||||||
|  | 	echo "web" | ||||||
|  | 	echo "web-embed" | ||||||
|  | 	echo "web-mobile" | ||||||
|  | 	echo "android" | ||||||
|  | 	echo "android-embed" | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | print_endpoints() | ||||||
|  | { | ||||||
|  | 	echo "Available endpoints:" | ||||||
|  | 	echo "browse" | ||||||
|  | 	echo "browse-continuation" | ||||||
|  | 	echo "next" | ||||||
|  | 	echo "next-continuation" | ||||||
|  | 	echo "player" | ||||||
|  | 	echo "search" | ||||||
|  | 	echo "resolve" | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | query_with_default() | ||||||
|  | { | ||||||
|  | 	prompt="$1" | ||||||
|  | 	default="$2" | ||||||
|  | 
 | ||||||
|  | 	printf "\n%s [%s]: " "$prompt" "$default" >&2 | ||||||
|  | 	read -r data | ||||||
|  | 
 | ||||||
|  | 	if [ -z "$data" ]; then | ||||||
|  | 		echo "$default" | ||||||
|  | 	else | ||||||
|  | 		echo "$data" | ||||||
|  | 	fi | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | query_with_error() | ||||||
|  | { | ||||||
|  | 	prompt="$1" | ||||||
|  | 	error_message="$2" | ||||||
|  | 
 | ||||||
|  | 	printf "\n%s []: " "$prompt" >&2 | ||||||
|  | 	read -r data | ||||||
|  | 
 | ||||||
|  | 	if [ -z "$data" ]; then | ||||||
|  | 		echo "Error: $error_message" | ||||||
|  | 		exit 1 | ||||||
|  | 	else | ||||||
|  | 		echo "$data" | ||||||
|  | 	fi | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | is_arg() | ||||||
|  | { | ||||||
|  | 	case $1 in | ||||||
|  | 		-c|--client)      true;; | ||||||
|  | 		-d|--data)        true;; | ||||||
|  | 		-e|--endpoint)    true;; | ||||||
|  | 		-h|--help)        true;; | ||||||
|  | 		-i|--interactive) true;; | ||||||
|  | 		-o|--output)      true;; | ||||||
|  | 		*)                false;; | ||||||
|  | 	esac | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # | ||||||
|  | # Parameters init | ||||||
|  | # | ||||||
|  | 
 | ||||||
|  | interactive=false | ||||||
|  | 
 | ||||||
|  | client_option="" | ||||||
|  | endpoint_option="" | ||||||
|  | 
 | ||||||
|  | data="" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # | ||||||
|  | # Interactive client selection | ||||||
|  | # | ||||||
|  | 
 | ||||||
|  | while :; do | ||||||
|  | 	# Exit if no more arguments to parse | ||||||
|  | 	if [ $# -eq 0 ]; then break; fi | ||||||
|  | 
 | ||||||
|  | 	case $1 in | ||||||
|  | 		-c|--client) | ||||||
|  | 			shift | ||||||
|  | 
 | ||||||
|  | 			if [ $# -eq 0 ] || is_arg "$1"; then | ||||||
|  | 				echo "Error: missing argument after -c/--client" | ||||||
|  | 				exit 2 | ||||||
|  | 			fi | ||||||
|  | 
 | ||||||
|  | 			client_option=$1 | ||||||
|  | 		;; | ||||||
|  | 
 | ||||||
|  | 		-d|--data) | ||||||
|  | 			shift | ||||||
|  | 
 | ||||||
|  | 			if [ $# -eq 0 ] || is_arg "$1"; then | ||||||
|  | 				echo "Error: missing argument after -d/--data" | ||||||
|  | 				exit 2 | ||||||
|  | 			fi | ||||||
|  | 
 | ||||||
|  | 			data=$1 | ||||||
|  | 		;; | ||||||
|  | 
 | ||||||
|  | 		-e|--endpoint) | ||||||
|  | 			shift | ||||||
|  | 
 | ||||||
|  | 			if [ $# -eq 0 ] || is_arg "$1"; then | ||||||
|  | 				echo "Error: missing argument after -e/--endpoint" | ||||||
|  | 				exit 2 | ||||||
|  | 			fi | ||||||
|  | 
 | ||||||
|  | 			endpoint_option=$1 | ||||||
|  | 		;; | ||||||
|  | 
 | ||||||
|  | 		-h|--help) | ||||||
|  | 			print_help | ||||||
|  | 			exit 0 | ||||||
|  | 		;; | ||||||
|  | 
 | ||||||
|  | 		-i|--interactive) | ||||||
|  | 			interactive=true | ||||||
|  | 		;; | ||||||
|  | 
 | ||||||
|  | 		-o|--output) | ||||||
|  | 			shift | ||||||
|  | 
 | ||||||
|  | 			if [ $# -eq 0 ] || is_arg "$1"; then | ||||||
|  | 				echo "Error: missing argument after -o/--output" | ||||||
|  | 				exit 2 | ||||||
|  | 			fi | ||||||
|  | 
 | ||||||
|  | 			output="$1" | ||||||
|  | 		;; | ||||||
|  | 
 | ||||||
|  | 		*) | ||||||
|  | 			echo "Error: unknown argument '$1'" | ||||||
|  | 			exit 2 | ||||||
|  | 		;; | ||||||
|  | 	esac | ||||||
|  | 
 | ||||||
|  | 	shift | ||||||
|  | done | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # | ||||||
|  | # Input validation | ||||||
|  | # | ||||||
|  | 
 | ||||||
|  | if [ ! -z "$data" ]; then | ||||||
|  | 	# Can't pass data in interactive mode | ||||||
|  | 	if [ $interactive = true ]; then | ||||||
|  | 		echo "Error: -d/--data can't be used with -i/--interactive" | ||||||
|  | 		exit 2 | ||||||
|  | 	fi | ||||||
|  | 
 | ||||||
|  | 	# Can't pass client in non-interactive mode (must be part of data) | ||||||
|  | 	if [ ! -z "$client_option" ]; then | ||||||
|  | 		echo "Error: -c/--client can't be used with -d/--data" | ||||||
|  | 		exit 2 | ||||||
|  | 	fi | ||||||
|  | 
 | ||||||
|  | 	# Endpoint must be given if non-interactive mode | ||||||
|  | 	if [ -z "$endpoint_option" ]; then | ||||||
|  | 		echo "Error: In non-interactive mode, an endpoint must be passed with -e/--endpoint" | ||||||
|  | 		exit 2 | ||||||
|  | 	fi | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | if [ -z "$data" ] && [ $interactive = false ]; then | ||||||
|  | 	# Data must be given if non-interactive mode | ||||||
|  | 	echo "Error: In non-interactive mode, data must be passed with -d/--data" | ||||||
|  | 	exit 2 | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | if [ -z "$output" ] && [ $interactive = true ]; then | ||||||
|  | 	printf "\nIt is recommended to use --output in interactive mode.\nContinue? [y/N]: " | ||||||
|  | 	read -r confirm | ||||||
|  | 
 | ||||||
|  | 	if [ -z "$confirm" ]; then confirm="n"; fi | ||||||
|  | 
 | ||||||
|  | 	case $confirm in | ||||||
|  | 		[Yy]|[Yy][Ee][Ss]) ;; | ||||||
|  | 		*) exit 0;; | ||||||
|  | 	esac | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # | ||||||
|  | # Client selection | ||||||
|  | # | ||||||
|  | 
 | ||||||
|  | if [ -z "$client_option" ]; then | ||||||
|  | 	client_option=$(query_with_default "Enter a client to use" "web") | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | case $client_option in | ||||||
|  | 	help) | ||||||
|  | 		print_clients | ||||||
|  | 		exit 0 | ||||||
|  | 	;; | ||||||
|  | 
 | ||||||
|  | 	web) | ||||||
|  | 		apikey="AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8" | ||||||
|  | 		client_name="WEB" | ||||||
|  | 		client_vers="2.20210721.00.00" | ||||||
|  | 	;; | ||||||
|  | 
 | ||||||
|  | 	web-embed) | ||||||
|  | 		apikey="AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8" | ||||||
|  | 		client_name="WEB_EMBEDDED_PLAYER" | ||||||
|  | 		client_vers="1.20210721.1.0" | ||||||
|  | 	;; | ||||||
|  | 
 | ||||||
|  | 	web-mobile) | ||||||
|  | 		apikey="AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8" | ||||||
|  | 		client_name="MWEB" | ||||||
|  | 		client_vers="2.20210726.08.00" | ||||||
|  | 	;; | ||||||
|  | 
 | ||||||
|  | 	android) | ||||||
|  | 		apikey="AIzaSyA8eiZmM1FaDVjRy-df2KTyQ_vz_yYM39w" | ||||||
|  | 		client_name="ANDROID" | ||||||
|  | 		client_vers="16.20" | ||||||
|  | 	;; | ||||||
|  | 
 | ||||||
|  | 	android-embed) | ||||||
|  | 		apikey="AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8" | ||||||
|  | 		client_name="ANDROID_EMBEDDED_PLAYER" | ||||||
|  | 		client_vers="16.20" | ||||||
|  | 	;; | ||||||
|  | 
 | ||||||
|  | 	*) | ||||||
|  | 		echo "Error: Unknown client '$client_option'" | ||||||
|  | 		echo "" | ||||||
|  | 		print_clients | ||||||
|  | 		exit 1 | ||||||
|  | 	;; | ||||||
|  | esac | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # | ||||||
|  | # Endpoint selection | ||||||
|  | # | ||||||
|  | 
 | ||||||
|  | if [ -z "$endpoint_option" ]; then | ||||||
|  | 	endpoint_option=$(query_with_default "Enter an endpoint to request" "") | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | case $endpoint_option in | ||||||
|  | 	help) | ||||||
|  | 		print_endpoints | ||||||
|  | 		exit 0 | ||||||
|  | 		;; | ||||||
|  | 
 | ||||||
|  | 	browse) | ||||||
|  | 		endpoint="youtubei/v1/browse" | ||||||
|  | 
 | ||||||
|  | 		if [ $interactive = true ]; then | ||||||
|  | 			browse_id=$(query_with_default "Enter browse ID" "UCXuqSBlHAE6Xw-yeJA0Tunw") | ||||||
|  | 			partial_data="\"browseId\":\"${browse_id}\"" | ||||||
|  | 		fi | ||||||
|  | 	;; | ||||||
|  | 
 | ||||||
|  | 	browse-cont*|browse-tok*) | ||||||
|  | 		endpoint="youtubei/v1/browse" | ||||||
|  | 
 | ||||||
|  | 		if [ $interactive = true ]; then | ||||||
|  | 			token=$(query_with_error "Enter continuation token" "token required") | ||||||
|  | 			partial_data="\"continuation\":\"${token}\"" | ||||||
|  | 		fi | ||||||
|  | 	;; | ||||||
|  | 
 | ||||||
|  | 	player|next) | ||||||
|  | 		endpoint="youtubei/v1/$endpoint_option" | ||||||
|  | 
 | ||||||
|  | 		if [ $interactive = true ]; then | ||||||
|  | 			vid=$(query_with_default "Enter video ID" "dQw4w9WgXcQ") | ||||||
|  | 			partial_data="\"videoId\":\"${vid}\"" | ||||||
|  | 
 | ||||||
|  | 		fi | ||||||
|  | 	;; | ||||||
|  | 
 | ||||||
|  | 	next-cont*|next-tok*) | ||||||
|  | 		endpoint="youtubei/v1/next" | ||||||
|  | 
 | ||||||
|  | 		if [ $interactive = true ]; then | ||||||
|  | 			token=$(query_with_error "Enter continuation token" "token required") | ||||||
|  | 			partial_data="\"continuation\":\"${token}\"" | ||||||
|  | 		fi | ||||||
|  | 	;; | ||||||
|  | 
 | ||||||
|  | 	search) | ||||||
|  | 		endpoint="youtubei/v1/search" | ||||||
|  | 
 | ||||||
|  | 		if [ $interactive = true ]; then | ||||||
|  | 			# Get search query, and escape backslashes and double quotes | ||||||
|  | 			query=$( | ||||||
|  | 				query_with_error "Enter your search query" "search term required" | | ||||||
|  | 				sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' | ||||||
|  | 			) | ||||||
|  | 			partial_data="\"query\":\"${query}\"" | ||||||
|  | 		fi | ||||||
|  | 	;; | ||||||
|  | 
 | ||||||
|  | 	resolve) | ||||||
|  | 		endpoint="navigation/resolve_url" | ||||||
|  | 
 | ||||||
|  | 		if [ $interactive = true ]; then | ||||||
|  | 			url=$(query_with_error "Enter URL" "URL required") | ||||||
|  | 			partial_data="\"url\":\"${url}\"" | ||||||
|  | 		fi | ||||||
|  | 	;; | ||||||
|  | 
 | ||||||
|  | 	*) | ||||||
|  | 		echo "Error: Unknown endpoint '$endpoint_option'" | ||||||
|  | 		echo "" | ||||||
|  | 		print_clients | ||||||
|  | 		exit 1 | ||||||
|  | 	;; | ||||||
|  | esac | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # | ||||||
|  | # Interactively request additional parameters for the supported endpoints | ||||||
|  | # | ||||||
|  | 
 | ||||||
|  | if [ $interactive = true ] | ||||||
|  | then | ||||||
|  | 	case $endpoint_option in | ||||||
|  | 
 | ||||||
|  | 	browse|player|search) | ||||||
|  | 		params=$(query_with_default "Enter optional parameters (base64-encoded protobuf)" "") | ||||||
|  | 
 | ||||||
|  | 		if [ ! -z "$params" ]; then | ||||||
|  | 			partial_data="${partial_data},\"params\":\"${params}\"" | ||||||
|  | 		fi | ||||||
|  | 	;; | ||||||
|  | 	esac | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | # new line | ||||||
|  | echo | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # | ||||||
|  | # Interactive language/region selection | ||||||
|  | # | ||||||
|  | 
 | ||||||
|  | if [ $interactive = true ]; then | ||||||
|  | 	hl=$(query_with_default "Enter content language (hl)" "en") | ||||||
|  | 	gl=$(query_with_default "Enter content region (gl)"   "US") | ||||||
|  | 
 | ||||||
|  | 	client="\"clientName\":\"${client_name}\",\"clientVersion\":\"${client_vers}\",\"hl\":\"${hl}\",\"gl\":\"${gl}\"" | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # | ||||||
|  | # Final command | ||||||
|  | # | ||||||
|  | 
 | ||||||
|  | if [ $interactive = true ]; then | ||||||
|  | 	data="{\"context\":{\"client\":{$client}},$partial_data}" | ||||||
|  | 
 | ||||||
|  | 	# Basic debug | ||||||
|  | 	echo "sending:" | ||||||
|  | 	echo "$data" | sed 's/{/{\n/g; s/}/\n}/g; s/,/,\n/g' | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | url="https://www.youtube.com/${endpoint}?key=${apikey}" | ||||||
|  | 
 | ||||||
|  | # Headers | ||||||
|  | hdr_ct='Content-Type: application/json; charset=utf-8' | ||||||
|  | hdr_ua='User-Agent: Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.0' | ||||||
|  | 
 | ||||||
|  | # Default to STDOUT if no output file was given | ||||||
|  | if [ -z "$output" ]; then output='-'; fi | ||||||
|  | 
 | ||||||
|  | # Run! | ||||||
|  | curl --compressed -o "$output" -H "$hdr_ct" -H "$hdr_ua" --data "$data" "$url" | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue