123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507 |
- #!/bin/bash
- menu="fzf"
- videoplayer="mpv --no-terminal"
- art="./art.txt"
- if [ ! -f $art ]; then
- art="/usr/share/ytui/art.txt"
- fi
- instance=https://invidious.namazso.eu
- RSSinstance=https://invidious.namazso.eu
- videoInstance=https://yewtu.be
- cachedir=/tmp/ytui
- if [ ! -d $cachedir ]; then
- mkdir $cachedir
- fi
- configdir=~/.config/ytui
- instances=$(cat $cachedir/instances)
- subsfeeds=$configdir/rssfeedupdate
- subsfeedsdir=$configdir/rssfeeds
- if [ ! -d $configdir ]; then
- mkdir $configdir
- fi
- freetubesubs=~/.config/FreeTube/profiles.db
- subsfile=$configdir/subscriptions.csv
- playimmediate=true
- green='\e[32m'
- blue='\e[34m'
- red='\e[31m'
- white='\e[0m'
- clear='\e[H\e[J'
- bold='\e[1m'
- PS3="> "
- settingsMenu () {
- heading 'Settings'
- echo -ne "
- f) get instances
- i) data instance [$instance]
- r) RSS instance [$RSSinstance]
- v) video instance [$videoInstance]
- s) import subscriptions from FreeTube
- y) import subscriptions from YouTube export
- q) Back
- > "
- read -n 1 option
- case $option in
- f) getInstances ; settingsMenu ;;
- i) changeInstance ; settingsMenu ;;
- r) changeRSSInstance ; settingsMenu ;;
- v) changeVideoInstance ; settingsMenu ;;
- s) importFreetube ;;
- y) importYoutube ;;
- $'\e'|'q') return ;;
- *) settingsMenu ;;
- esac
- }
- getInstances() {
- heading "Finding Instances"
- all_instances="$(echo "$(cat $configdir/instances)
- $(get 'https://api.invidious.io/instances.json' | \
- jq -r '.[] | .[1].uri' | \
- sed 's#/$##' | sed '/\.onion$/d')" | sort | uniq)"
- while read uri ; do
- testSite $uri
- done <<< $all_instances
- echo "$instances" > $cachedir/instances
- }
- testSite() {
- echo -ne "$white$uri\t"
- wget --spider --timeout=0.3 -t 3 -q $uri
- if [ $? -eq 0 ]; then
- echo -e "$green Online"
- instances="$instances
- $uri"
- else
- echo -e "$red Offline"
- fi;
- }
- changeInstance () {
- heading "Choose an instance"
- instance_tmp=$instance
- instance=$($menu <<< "$instances")
- if [ -z "$instance" ]; then
- instance=$instance_tmp
- fi
- }
- changeRSSInstance () {
- heading "Choose an instance"
- instance_tmp=$RSSinstance
- RSSinstance=$($menu <<< "$instances")
- if [ -z "$RSSinstance" ]; then
- RSSinstance=$instance_tmp
- fi
- }
- changeVideoInstance () {
- heading "Choose an instance"
- videoInstance_tmp=$videoInstance
- videoInstance=$($menu <<< "$instances")
- if [ -z "$videoInstance" ]; then
- videoInstance=$videoInstance_tmp
- fi
- }
- importFreetube () {
- if [ ! -f $freetubesubs ]; then
- echo "Cannot find freetube Subscriptions in $freetubesubs"
- echo -ne "Enter a different file to try >"
- read freetubesubs
- importFreetube
- return;
- fi
- echo "$(jq -r '.subscriptions[] | .id + "," + .name' $freetubesubs)" >> $subsfile
- echo -e "\nImported" $(jq -r '.subscriptions[] | .id' $freetubesubs | wc -l) items
- read -n 1 -t 1
- }
- importYoutube () {
- echo -ne "path to youtube subs >"
- read youtubesubs
- while [ ! -f "$youtubesubs" ]; do
- echo "Cannot find file $youtubesubs"
- echo -ne "Enter a different file to try >"
- read youtubesubs
- done
- jq -r '.[] | .snippet.resourceId.channelId + "," + .snippet.title' $youtubesubs >> $subsfile
- echo -e "\nImported" $(jq -r '.[] | .snippet.channelId' $youtubesubs | wc -l) items
- read -n 1 -t 1
- }
- invidiousGet () {
- get $instance/api/v1/$1
- return $?
- }
- get () {
- hash=$(echo $1 | md5sum | cut -c 1-32)
- if [ ! -f "$cachedir/$hash.json" ]; then
- wget $1 -O $cachedir/$hash.json -a $cachedir/logs
- error=$?
- if [ ! $error -eq 0 ]; then
- rm $cachedir/$hash.json
- return $error
- fi;
- fi
- cat $cachedir/$hash.json
- }
- invidiousRefresh () {
- refresh $instance/api/v1/$1
- }
- refresh () {
- hash=$(echo $1 | md5sum | cut -c 1-32)
- filename="$cachedir/$hash.json"
- if [ -f $filename ]; then
- rm $filename
- fi
- }
- searchChannel () {
- echo -ne "\nSearching for a channel. Blank to cancel.\e[1F? "
- read channelSearch
- channelSearch=$(echo $channelSearch | sed 's/ /+/g')
- if [ ! -z $channelSearch ]; then
- pickChannel "search?q=$channelSearch&type=channel" 0
- fi
- }
- searchVideo () {
- echo -ne "\nSearching for a video. Blank to cancel.\e[1F/ "
- read search
- search=$(echo $search | sed 's/ /+/g')
- if [ ! -z $search ]; then
- pickVideo "search?q=$search&type=video" 0
- fi
- }
- pickChannel () {
- heading "Loading $1"
- channelsData=$(invidiousGet $1)
- error=$?
- if [ ! $error -eq 0 ]; then
- echo "wget error $error. Maybe try another instance."
- read -n 1
- return $error
- fi
- pickChannelJSON "$(jq -r '.' <<< $channelsData)" ||
- invidiousRefresh $1
- }
- pickChannelJSON () {
- heading "Pick a channel"
- options=$(jq -r '.[] | .author + "\t" + (.subCount | tostring) + "\t" + .authorId' <<< $1 |
- column -t -s$'\t')
- while true; do
- id=$(echo "$options" | $menu | sed 's/^\(.*\) \(.*\)$/\2/')
- if [ -z "$id" ]; then
- break
- fi
- channelMenu $id
- done
- }
- heading () {
- echo -e $clear$blue$bold$1$white
- }
- channelMenu () {
- heading "Loading... channels/$1"
- channelData=$(invidiousGet channels/$1)
- error=$?
- if [ ! $error -eq 0 ]; then
- echo "wget error $error. Maybe try another instance."
- read -n 1
- return $error
- fi
- channelName=$(echo $channelData | jq -r '.author')
- heading "$channelName Channel"
- echo $1
- grep -Fq "$1" "$subsfile" && echo "Subscribed" || echo "Not Subscribed"
- echo -ne $white
- echo $channelData | jq -r '.description'
- echo -ne "
- What would you like to see?
- l) Latest Videos
- v) Popular Videos
- p) Playlists
- w) Subscribe
- q) Back
- > "
- read -n 1 option
- case $option in
- ,) settingsMenu ;
- channelMenu $1 ;;
- /) searchVideo ; channelMenu $1 ;;
- '?') searchChannel ; channelMenu $1 ;;
- l) pickVideo "channels/videos/$1?sort_by=newest"
- channelMenu $1
- ;;
- v) pickVideo "channels/videos/$1?sort_by=popular"
- channelMenu $1
- ;;
- p) playlists $1 ; channelmenu $1 ;;
- w) echo "$1,$channelName" >> $subsfile
- channelMenu $1
- ;;
- r) invidiousRefresh channels/$1 ""
- channelMenu $1
- ;;
- $'\e'|'q') return ;;
- *) channelMenu $1 ;;
- esac
- }
- playlists () {
- heading Loading "channels/$1/playlists"
- playlistData=$(invidiousGet "channels/$1/playlists")
- error=$?
- if [ ! $error -eq 0 ]; then
- echo "wget error $error. Maybe try another instance."
- read -n 1
- return $error
- fi
- choice=$( echo $playlistData | jq -r '.playlists[] | .title + "\t" + .playlistId' | column -t -s$'\t' | fzf | awk '{ print $NF }' )
- playlistMenu $choice
- }
- playlistMenu () {
- heading Loading "playlists/$1"
- playlistData=$(invidiousGet "playlists/$1")
- error=$?
- if [ ! $error -eq 0 ]; then
- echo "wget error $error. Maybe try another instance."
- read -n 1
- return $error
- fi
- while true; do
- choice=$( echo $playlistData | jq -r '.videos[] | .title + "\t" + .videoId' | column -t -s$'\t' | fzf | awk '{ print $NF }' )
- if [ -z "$choice" ]; then
- break
- fi
- watchVideo $choice
- done
- }
- # TODO: multiple pages of videos
- pickVideo () {
- heading Loading $1
- data=$(invidiousGet $1)
- error=$?
- if [ ! $error -eq 0 ]; then
- echo "wget error $error. Maybe try another instance."
- read -n 1
- return $error
- fi
- pickVideoJSON "$(jq -r '.' <<< $data)"
- }
- pickVideoJSON () {
- heading "Pick a video"
- options=$(jq -r '.[] | (.title) + "\t" +
- (.author) + "\t" +
- (if (.lengthSeconds >= 3600) then
- (.lengthSeconds/3600 | floor | tostring) + ":" + (if (.lengthSeconds%3600/60<=10) then "0" else "" end) + (.lengthSeconds%3600/60 | floor | tostring) + ":" + (if (.lengthSeconds%60<=10) then "0" else "" end) + (.lengthSeconds%60 | tostring)
- else
- (.lengthSeconds/60 | floor | tostring) + ":" + (if (.lengthSeconds%60<=10) then "0" else "" end) + (.lengthSeconds%60 | tostring)
- end) + "\t" +
- (if .viewCount > 1000000 then
- (.viewCount / 1000000 | floor | tostring) + "M"
- elif .viewCount > 1000 then
- (.viewCount / 1000 | floor | tostring) + "K"
- else (.viewCount | tostring)
- end + "\t" + .videoId)' <<< $1 |
- column -t -s$'\t' -R3,4)
- while true; do
- id=$(echo "$options" | $menu | sed 's/^\(.*\) \(.*\)$/\2/')
- if [ -z "$id" ]; then
- break
- fi
- watchVideo $id
- done
- }
- watchVideo () {
- if [ -z "$1" ]; then
- return
- fi
- url="$videoInstance/watch?v=$1"
- if [ $playimmediate = true ]; then
- $videoplayer $url &
- fi
- if [ -z "$2" ]; then
- heading "Loading $instance/api/v1/videos/$1 ..."
- videoData=$(invidiousGet "videos/$1")
- error=$?
- if [ ! $error -eq 0 ]; then
- echo "wget error $error. Maybe try another instance."
- read -n 1
- return $error
- fi
- else
- videoData=$2
- fi
- while true; do
- heading "$(echo $videoData | jq -r '.title')"
- echo $url
- echo -ne "
- p) Play Video
- d) description
- c) View Channel
- n) Related Videos
- r) Refresh page
- q) Back
- > "
- read -n 1 vidNum
- case $vidNum in
- ,) settingsMenu ;;
- /) searchVideo;;
- '?') searchChannel ;;
- c)
- channelMenu $(jq -r '.authorId' <<< $videoData)
- ;;
- n)
- pickVideoJSON "$(jq -r '.recommendedVideos' <<<$videoData)"
- ;;
- d)
- echo $videoData | jq -r '.description' | less
- ;;
- p)
- echo -e "\nStarting Video"
- $videoplayer $url &
- ;;
- r)
- invidiousRefresh videos/$1
- watchVideo $1 $2
- break
- ;;
- $'\e'|'q') break ;;
- *) echo 'try again' ;;
- esac
- done
- }
- subscriptionsMenu () {
- heading Subscriptions
- echo -ne "
- What do you want to do?
- c) View Subscribed Channels
- l) Latest Videos
- r) Reload Videos
- q) Exit
- > "
- read -n 1 option
- case $option in
- ,) settingsMenu ; subscriptionsMenu ;;
- /) searchVideo ; subscriptionsMenu ;;
- '?') searchChannel ; subscriptionsMenu ;;
- c) subscribedChannels ; subscriptionsMenu ;;
- l) subscriptionsLatest ; subscriptionsMenu ;;
- r) subscriptionsReload ; subscriptionsMenu ;;
- $'\e'|'q') echo -ne $clear ;;
- esac
- }
- subscriptionsReload () {
- heading Reloading
- echo -e "sfeedpath=$subsfeedsdir \n feeds() {" > $subsfeeds
- cat $configdir/subscriptions.csv | \
- awk -F ',' '{print "feed \""$1"\"" " \"'$RSSinstance'/feed/channel/"$1"\""}' \
- >> $subsfeeds
- echo "}" >> $subsfeeds
- sfeed_update $subsfeeds
- }
- subscriptionsLatest () {
- while true; do
- if [ -z "$1" ]; then
- choice=$(awk -F'\t' '!seen[$6]++ {OFS="\t|\t";
- $1=strftime("%Y-%m-%d", $1);
- sub(".*/", "", FILENAME);
- channelid = FILENAME;
- print $1, $7, $2, $3, channelid}' "$subsfeedsdir/"* | sort -r | column -t -s$'\t' -T4 | $menu)
- else
- choice=$(awk -F'\t' '!seen[$6]++ {OFS="\t|\t";
- $1=strftime("%Y-%m-%d", $1);
- sub(".*/", "", FILENAME);
- channelid = FILENAME;
- print $1, $7, $2, $3, channelid}' "$subsfeedsdir/$1" | sort -r | column -t -s$'\t' -T4 | $menu)
- fi
- if [ ! -z "$choice" ]; then
- watchVideo "$(sed 's@^.*/watch?v=\(.*\)@\1@' <<< $choice)" \
- "{ \"title\":\"$(awk -F'|' '{ print $3 }' <<< $choice)\",
- \"authorId\":\"$(awk -F'|' '{ print $5 }' <<< $choice)\"}"
- else
- break
- fi
- done
- }
- subscribedChannels () {
- while true; do
- heading Subscriptions
- subs=$(cat $configdir/subscriptions.csv)
- channel=$(awk -F, '{OFS="\t"; print $1, $2 }' <<< $subs | column -t -s$'\t' | $menu | awk '{print $1}')
- if [ ! -z "$channel" ]; then
- subscriptionsLatest "$channel" || channelMenu "$channel"
- else
- break
- fi
- done
- }
- menu () {
- echo -ne $clear$bold$red
- cat $art
- echo -ne $white
- echo -ne "
- What do you want to do?
- ,) Settings
- ?) Search for channel
- /) Search for video
- s) Subscriptions
- q) Exit
- > "
- read -n 1 option
- case $option in
- ,) settingsMenu ; menu ;;
- '?') searchChannel ; menu ;;
- /) searchVideo ; menu ;;
- s) subscriptionsMenu ; menu ;;
- r) sfeed_update $subsfeeds ;;
- $'\e'|'q') echo -ne $clear ; exit ;;
- *) menu ;;
- esac
- }
- if [ ! -z $1 ]; then
- if [[ $1 =~ [-_a-zA-Z0-9]{11} ]]; then
- watchVideo $BASH_REMATCH
- menu
- exit
- fi
- echo "could not find video ID in $1"
- read -n 1
- exit
- else
- menu
- fi
|