diff --git a/README.md b/README.md index ae3bc11..630bb1a 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,78 @@ -# Shelltube -Full-featured POSIX shell YouTube client. +#Shelltube + +Shelltube is a collection of (pretty POSIX) shell scripts to +browse YouTube quickly, efficiently, and without the bloat +most command-line clients require. + +Shelltube is written in pure shell; its only dependencies +are any modern shell (pdksh, bash, zsh), curl/wget, and +vlc/mplayer/kaffeine. + +Even these dependencies, though, can easily be changed. If, +for example, you don't have vlc, you can just modify a +single line and be good-to-go using another player. + +Also, Shelltube doesn't use the YouTube API at all. This +avoids the annoying red-tapey stuff that goes with it-- IDs, +registration, quotas-- but has some disadvantages. We'll +power through the downsides, though! :) + + +USAGE: +------- +Shelltube is quite simple to use; this tutorial will go over +the usage of the wrapper script, shelltube.sh. +When running the script, you'll see a prompt: + >> + +In this prompt you can type any of the following commands: +(!) about View the about page. +(cls) clear Clear the screen. +(dl) download [URL] Download the selected/specified video. +exit Exit Shelltube. +(?) help Display this message. +(md) metadata [URL] Display selected/specified video's MD. +(/) search {term} Perform a search. +(str) stream [URL] Stream the selected/specified video. +(sel) video {URL} Select video based on URL or ID. + +In (parenthesis), smaller aliases for the commands are written. +In {curly brackets}, required arguments are written. +In [brackets], optional arguments are written. + +You can use Shelltube in one of two ways (or both): + A) By selecting a video and then doing something with it + B) By doing something and specifying the video + +Method A entails using either the "video" or "search" command +to select a video, which will then be displayed before the prompt +like so: +$VIDEO_ID_HERE >> +When a video is selected, you use the "download", "stream", or +"metadata" commands without arguments to act on the video. + +Method B entails just using the "download", "stream", or +"metadata" commands while using a URL or video ID as an argument. +For example: + >> download $VIDEO_ID_HERE + + +You could opt to not use this interactive wrapper script and instead +just use the yt-down.sh, yt-search.sh, and yt-metadata.sh scripts +on their own. + +They are each pretty simple, and you can read their USAGE messages +at the top of each script. + + +LICENSING: +----------- +All of ST is released under the ISC license +(https://opensource.org/licenses/ISC) +except for the yt-down.sh script, which is released under the GPLv2 +(https://www.gnu.org/licenses/gpl-2.0.html) + +CREDIT: +-------- +jadedctrl wrote most of ST, but iluaster wrote almost all of +yt-down.sh diff --git a/shelltube.sh b/shelltube.sh new file mode 100644 index 0000000..8d3d187 --- /dev/null +++ b/shelltube.sh @@ -0,0 +1,127 @@ +#################### +# Name: shelltube.sh +# Date: 2016-12-11 +# Lisc: ISC +# Main: jadedctrl +# Desc: Full-shell YT client that +# avoids the YT API. +##################### + +function search() { + output="/tmp/yt-search-$RANDOM" + sh yt-search.sh -i "$1" $output + selected_video=$(cat $output) + metadata +} + +function interactive() { + get_input +} + +function about() { + echo -e "\033[0;35mShelltube v0.3" + echo -e "\033[0;32mDesc: \033[0;34mYT client written in shell." + echo -e "\033[0;32mMain: \033[0;34mjadedml@openmailbox.org" + echo -e "\033[0;32mLisc: \033[0;34mISC; yt-down.sh & yt-stream.sh GPLv2\033[0m" +} + +function metadata() { + sh yt-meta.sh "$selected_video" +} + +function download() { + sh yt-down.sh "$selected_video" +} + +function stream() { + sh yt-down.sh -s "$selected_video" +} + +function get_input() { + printf "\033[0;34m$selected_video \033[0;32m>>\033[0m " + read -r n + if [ "$n" == "help" ] || [ "$n" == "?" ] + then + help + interactive + elif echo "$n" | grep "^search " > /dev/null + then + search "$(echo "$n" | sed 's/search //')" + interactive + elif echo "$n" | grep "^/" > /dev/null + then + search "$(echo "$n" | sed 's^/^^')" + interactive + elif echo "$n" | grep "^/ " > /dev/null + then + search "$(echo "$n" | sed 's^/ ^^')" + interactive + elif echo "$n" | grep "^video " > /dev/null + then + if echo "$n" | grep "youtube.com" + then + selected_video="$(echo "$n" | sed 's/.*watch?v=//')" + else + selected_video="$(echo "$n" | sed 's/sel //')" + fi + metadata + interactive + elif echo "$n" | grep "^sel " > /dev/null + then + if echo "$n" | grep "youtube.com" + then + selected_video="$(echo "$n" | sed 's/.*watch?v=//')" + else + selected_video="$(echo "$n" | sed 's/sel //')" + fi + metadata + interactive + elif [ "$n" == "download" ] || [ "$n" == "dl" ] + then + download + interactive + elif [ "$n" == "metadata" ] || [ "$n" == "md" ] + then + metadata + interactive + elif [ "$n" == "stream" ] || [ "$n" == "str" ] + then + stream + interactive + elif [ "$n" == "about" ] || [ "$n" == "!" ] + then + about + interactive + elif [ "$n" == "clear" ] || [ "$n" == "cls" ] + then + clear + interactive + elif [ "$n" == "exit" ] + then + rm /tmp/yt-* + exit + else + get_input + fi +} + +function help() { + echo "about | ! View the about page." + echo "clear | cls Clear the screen." + echo "download | dl Download the selected video." + echo "exit | ctrl+c Exit Shelltube." + echo "help | ? Display this message." + echo "metadata | md Display selected video's metadata." + echo "search | / Perform a search." + echo "stream | str Stream the selected video." + echo "video | sel Select video based on URL or ID." + echo "Note about usage:" + echo "Both 'video ID; download' and 'download ID' are valid." + echo "You don't need to select a video to run commands on it," + echo "but if you use metadata, download, or stream on an" + echo "unselected video you must specify the ID or URL after the" + echo "command." +} + +echo -e "\033[0;35mShelltube v0.3" +interactive diff --git a/yt-down.sh b/yt-down.sh new file mode 100644 index 0000000..4f92e17 --- /dev/null +++ b/yt-down.sh @@ -0,0 +1,129 @@ +#!/bin/sh +##################### +# Name: yt-download.sh +# Date: 2016-12-10 +# Lisc: GPLv2 +# Auth: iluaster +# Main: jadedctrl +# Desc: Fetches YT videos. +##################### + +# Based on https://github.com/iluaster/Youtube_downloader +# Usage: yt-download.sh $video_id + +if [ "$1" == "-s" ] +then + stream_mode=1 + id=$2 +else + stream_mode=0 + id=$1 +fi + +if echo "$id" | grep "youtube.com" +then + id="$(echo $id | sed 's/.*video_id=//')" +elif [ -z $id ] +then + echo "No video specified." + exit 1 +else + id="$id" +fi + +name="http://www.youtube.com/get_video_info?video_id=$id" + +declare -i line=0 + +function select_option () +{ + echo "Formats:" + for i in $(cat /tmp/video_type_option.txt) + do + line=$((line+1)) + echo "${line}. $i" + done + printf "\033[0;32m>>\033[0m " + read -r n + + if [ "$n" -le "$line" ]; + then + head -n "$n" /tmp/tmp3.txt | tail -n 1 > /tmp/tmp4.txt + else + echo "Input Error!" + line=0 + select_option + fi +} + + +if type "wget" &> /dev/null +then + wget -q "$name" -O "/tmp/${id}_url.txt" +elif type "curl" &> /dev/null +then + curl -s "$name" -o "/tmp/${id}_url.txt" +else + echo "Please install wget or curl." + exit 1 +fi + +# Cut and filter mp4 url + +cp "/tmp/${id}_url.txt" /tmp/tmp2.txt +sed -e 's/&/\n/g' /tmp/tmp2.txt| grep 'url_encoded_fmt_stream_map'> /tmp/tmp3.txt +sed -i -e 's/%2C/,/g' /tmp/tmp3.txt +sed -i -e 's/,/\n/g' /tmp/tmp3.txt + +# Print out total video format name and quality +perl -ne 'print "$2,$1\n" if /quality%3D(.*?)%.*video%252F(.*?)(%|\n)/' /tmp/tmp3.txt > /tmp/video_type_option.txt + +# If video format name is prior to quality +perl -ne 'print "$1,$2\n" if /video%252F(.*?)%.*quality%3D(.*?)(%|\n)/' /tmp/tmp3.txt >> /tmp/video_type_option.txt +sed -i -e 's/x-flv/flv/g' /tmp/video_type_option.txt + +select_option + +# Set file extension name variable and video quality variable +extension_name=$(head -n "$n" /tmp/video_type_option.txt | tail -n 1 | cut -d "," -f 1) +quality_name=$(head -n "$n" /tmp/video_type_option.txt | tail -n 1 | cut -d "," -f 2) + +sed -i -e 's/%26/\&/g' /tmp/tmp4.txt +sed -i -e 's/&/\n/g' /tmp/tmp4.txt +grep 'http' /tmp/tmp4.txt > /tmp/tmp5.txt +grep 'sig%3D' /tmp/tmp4.txt >> /tmp/tmp5.txt +perl -pe 's/\n//g' /tmp/tmp5.txt | sed -e 's/sig%3D/\&signature%3D/g' > /tmp/tmp6.txt +sed -i -e 's/url%3D//g' /tmp/tmp6.txt +# url decoding +cat /tmp/tmp6.txt | sed -e 's/%25/%/g' -e 's/%25/%/g' -e 's/%3A/:/g' -e 's/%2F/\//g' -e 's/%3F/\?/g' -e 's/%3D/=/g' -e 's/%26/\&/g' > /tmp/tmp7.txt + + +if [ $stream_mode -eq 1 ] +then + if type "vlc" &> /dev/null + then + vlc $(cat /tmp/tmp7.txt) + elif type "mplayer" &> /dev/null + then + mplayer $(cat /tmp/tmp7.txt) + elif type "kaffeine" &> /dev/null + then + kaffeine $(cat /tmp/tmp7.txt) + else + echo "Please install either vlc, mplayer, or kaffeine." + exit 1 + fi +else + if type "wget" &> /dev/null + then + wget --show-progress -qi /tmp/tmp7.txt -O "${id_name}_${quality_name}.${extension_name}" + elif type "curl" &> /dev/null + then + curl -# $(cat /tmp/tmp7.txt) -o "${id_name}_${quality_name}.${extension_name}" + else + echo "Please install wget or curl." + exit 1 + fi +fi + +rm -f /tmp/tmp[2-7].txt /tmp/${id}_url.txt /tmp/video_type_option.txt diff --git a/yt-meta.sh b/yt-meta.sh new file mode 100644 index 0000000..a4a355d --- /dev/null +++ b/yt-meta.sh @@ -0,0 +1,42 @@ +##################### +# Name: yt-meta.sh +# Date: 2016-12-10 +# Lisc: ISC +# Main: jadedctrl +# Desc: Fetches metadata about +# a specific YT video. +##################### + +# Usage: yt-meta.sh $video_id + +if echo "$1" | grep "youtube.com" +then + id="$(echo $1 | sed 's/.*video_id=//')" +elif [ -z $1 ] +then + "No video specified." + exit 1 +else + id="$1" +fi + + +video_file="/tmp/yt-video_$RANDOM" +if type "wget" &> /dev/null +then + wget -q https://youtube.com/watch?v=$id -O $video_file + wget -q https://youtube.com/results?search_query=$id -O $video_file.1 +elif type "curl" &> /dev/null +then + curl -s https://youtube.com/watch?v=$id -o $video_file + curl -s https://youtube.com/results?search_query=$id -o $video_file.1 +fi + +# Now for displaying the metadata +title="$(grep "\"title\":\"" $video_file | sed 's/.*"title":"//' | sed 's/".*//')" +author="$(grep "\"author\":\"" $video_file | sed 's/.*"author":"//' | sed 's/".*//')" +views="$(grep "\"view_count\":\"" $video_file | sed 's/.*"view_count":"//' | sed 's/".*//')" +duration="$(grep ">>\033[0m " + read -r n + + if [ "$n" == "exit" ] + then + exit + fi + + test $n -ge 0 &> /dev/null + + if [ $? -gt 1 ] + then + echo "Bad input, mate. Type in a valid number or 'exit'." + get_input + elif [ $n -gt 20 ] + then + echo "Out of range. Type in a valid number or 'exit'." + get_input + elif [ $n -gt 0 ] && [ $n -lt 20 ] + then + sed -n ${n}p $temp_file | sed 's/.* $output + else + echo "Bad input, mate. Type in a valid number or 'exit'." + get_input + fi +} + +# If Google adds any extra features or changes the webpage +# layout, this script'll break immediately, haha. +# ... But at least we aren't using their API, right? + +search_file="/tmp/yt-search_$RANDOM" + +if type "wget" &> /dev/null +then + wget -q https://youtube.com/results?search_query=$query -O $search_file +elif type "curl" &> /dev/null +then + curl -s https://youtube.com/results?search_query=$query -o $search_file +fi + +# Now for displaying the search results +temp_file="/tmp/yt-result_$RANDOM" +grep " $temp_file +item_num=0 +cat $temp_file | while IFS='' read -r CUR_LINE +do + item_num=$(($item_num+1)) + # These tags trip up 'title=' and '" >' queries later on. Strip 'em. + LINE="$(echo $CUR_LINE | sed 's/.*//')" + LINE="$(echo $LINE | sed 's/views<\/li>.*//')" + LINE="$(echo $LINE | sed 's/title="Verified"//')" + + if [ $row -eq 1 ] + then + color='\033[1;34m' + color2='\033[1;34m' + row=0 + elif [ $row -eq 0 ] + then + color='\033[1;31m' + color2='\033[1;31m' + row=1 + fi + + if echo "$LINE" | grep "View full playlist" > /dev/null + then + type="Playlist" + if [ $interactive_mode -eq 1 ] + then + printf "${color}$item_num. " + fi + title=$(echo "$LINE" | sed 's/.*title="//' | sed 's/".*//') + items=$(echo "$LINE" | sed 's/.*View full playlist (//' | sed 's/).*//') + echo -e "${color}$title" + echo -e "${color2}$type | $items | $itemid" + else + type="Video" + duration=$(echo "$LINE" | sed 's/.*Duration: //' | sed 's/\..*//') + itemid=$(echo "$LINE" | sed 's/.* /dev/null + then + # For /channel/ users + author=$(echo "$LINE" | sed 's/.*" >//' | sed 's/<\/a>.*//') + else + # For /user/ users + author=$(echo "$LINE" | sed 's/.*\/user\///' | sed 's/".*//') + fi + + if [ $interactive_mode -eq 1 ] + then + if [ $item_num -lt 10 ] + then + printf "${color}$item_num. " + else + printf "${color}$item_num. " + fi + echo -e "${color}$title${color2}" + printf " " + fi + i=0 + while [ $i -lt 16 ] + do + i=$((i+1)) + char=$(echo $author | cut -c$i) + if [ -z $char ] + then + printf " " + else + printf "$char" + fi + done + printf " | " + i=0 + while [ $i -lt 5 ] + do + i=$((i+1)) + char=$(echo $duration | cut -c$i) + if [ -z $char ] + then + printf " " + else + printf "$char" + fi + done + printf " | $itemid\n" + fi +done + +if [ $interactive_mode -eq 1 ] +then + get_input +fi + + +rm $temp_file