Compare commits

...

10 Commits

Author SHA1 Message Date
wvr
66f2f852f2 initial 2023-05-14 16:07:03 -05:00
0c273bb02c remove unneeded shellcheck disable 2021-05-10 08:58:50 -05:00
a2846eb4a3 $SUBS_MENU_PROG should be internally quoted, closes #7 2021-05-10 08:56:05 -05:00
6be68d0538 fix readme pic 2021-05-09 15:54:22 -05:00
11c2cf9b1d another are edgecase fix if yt returns junk data on getvids 2021-05-09 15:54:16 -05:00
b72d79b987 catch more edge cases, bugfixes 2021-05-09 15:34:34 -05:00
Theo Henson
89fe982535 Substitute HTML escape codes when piping into menu (#5)
PR: Substitute HTML escape codes when piping into menu
2021-05-07 20:50:11 -07:00
df1d74b2fc default to usage on bad arguments, require no args to run
fixup
2021-05-04 20:02:03 -05:00
4fd4316e51 fix travis 2021-05-02 00:05:24 -05:00
ad67e8279f add [-n] option, bugfixes 2021-05-02 00:03:30 -05:00
5 changed files with 94 additions and 45 deletions

0
.travis.yml Normal file → Executable file
View File

0
LICENSE Normal file → Executable file
View File

2
README.md Normal file → Executable file
View File

@@ -14,7 +14,7 @@ I'm a fan of command line and "doing things yourself".
I also don't like having a Google account, but still want to keep track of subscriptions. I also don't like having a Google account, but still want to keep track of subscriptions.
Thus [subs](http://github.com/mitchweaver/subs) was born. Thus [subs](http://github.com/mitchweaver/subs) was born.
![subs_dmenu](https://wvr.sh/u/tXCc.png) ![subs_dmenu](https://i.imgur.com/8vD66zO.png)
## Environment ## Environment

0
makefile Normal file → Executable file
View File

137
subs
View File

@@ -1,4 +1,12 @@
#!/bin/sh #!/bin/sh
# █████
# ▒▒███
# █████ █████ ████ ▒███████ █████
# ███▒▒ ▒▒███ ▒███ ▒███▒▒███ ███▒▒
# ▒▒█████ ▒███ ▒███ ▒███ ▒███▒▒█████
# ▒▒▒▒███ ▒███ ▒███ ▒███ ▒███ ▒▒▒▒███
# ██████ ▒▒████████ ████████ ██████
# ▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒
# #
# Watch your youtube subscriptions without a youtube account # Watch your youtube subscriptions without a youtube account
# via curl, dmenu, mpv and basic unix commands. # via curl, dmenu, mpv and basic unix commands.
@@ -9,16 +17,22 @@
# For more information and examples, see: # For more information and examples, see:
# http://github.com/mitchweaver/subs # http://github.com/mitchweaver/subs
# #
# >> note: this is highly experimental / janky, it can and will break <<
#
# -/-/-/-/- Settings -/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/ # -/-/-/-/- Settings -/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/
: "${SUBS_FILE:=~/files/subs.txt}" : "${SUBS_FILE:=~/files/subs.txt}"
: "${SUBS_MENU_PROG:='dmenu -p Subs:'}" : "${SUBS_MENU_PROG:=dmenu -l 18 -p Subs:}"
: "${SUBS:=${XDG_CACHE_HOME:-~/.cache}/subs}" : "${SUBS:=${XDG_CACHE_HOME:-~/.cache}/subs}"
: "${SUBS_LINKS:=$SUBS/links}" : "${SUBS_LINKS:=$SUBS/links}"
: "${SUBS_CACHE:=$SUBS/cache}" : "${SUBS_CACHE:=$SUBS/cache}"
: "${SUBS_SLEEP_VALUE:=0.05}" # raise this if you experience problems : "${SUBS_SLEEP_VALUE:=1}" # raise this if you experience problems
: "${SUBS_DAEMON_INTERVAL:=600}"
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SEP=^^^^^ # shouldn't need to change this SEP=^^^^^ # shouldn't need to change this
# -/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/ # -/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/
export LC_ALL=C # this speeds things up a bit but can cause
# issues for titles in foreign languages
# -/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/
die() { die() {
>&2 printf '%s\n' "$*" >&2 printf '%s\n' "$*"
@@ -26,7 +40,7 @@ die() {
} }
usage() { usage() {
die 'Usage: subs [-m no-video] [-g gen-links] [-u update-cache] [-d daemonize]' die 'Usage: subs [-m no-video] [-g gen-links] [-u update-cache] [-d daemonize] [-n dont-play]'
} }
# -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* # -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
@@ -40,6 +54,7 @@ gen_links() {
count=0 count=0
total=$(sed -e '/^$/d' -e '/^#/d' <"$SUBS_FILE" | wc -l) total=$(sed -e '/^$/d' -e '/^#/d' <"$SUBS_FILE" | wc -l)
total=${total##* }
while read -r line ; do while read -r line ; do
@@ -55,20 +70,31 @@ gen_links() {
case $line in case $line in
UC*) UC*)
# YT channel IDs always begin with 'UC' and are 24 chars long # YT channel IDs always begin with 'UC' and are 24 chars long
printf "[%s/%s] using channel ID '%s' for xml link\n" "$count" "$total" "$line" if [ ${#line} -eq 24 ] ; then
printf "[%s/%s] using channel ID '%s' for xml link\n" "$count" "$total" "$line"
[ ${#line} -eq 24 ] &&
printf 'https://youtube.com/feeds/videos.xml?%s\n' \ printf 'https://youtube.com/feeds/videos.xml?%s\n' \
"channel_id=$line" >>"$SUBS_LINKS" "channel_id=$line" >>"$SUBS_LINKS"
else
>&2 printf 'Error: cannot determine channel for %s\n' "$line"
fi
;; ;;
*) *)
# otherwise we are given a username, we must find out its channel ID # otherwise we are given a username, we must find out its channel ID
printf "fetching channel ID for %s...\n" "$line" printf "Fetching channel ID for %s..." "$line"
curl -sfL --retry 10 "https://youtube.com/user/$line/about" | \ data=$(curl -sL --retry 10 "https://youtube.com/user/$line/about")
if printf '%s\n' "$data" | grep '404 Not Found' >/dev/null ; then
>&2 printf '\n[ERROR]: Could not determine channel for %s... 404\n' "$line"
>&2 printf '[%s] %s\n' "$(date)" "$line" >> "$SUBS"/ERRORS.log
return 1
fi
printf '%s\n' "$data" | \
while read -r line ; do while read -r line ; do
case $line in case $line in
*channel/UC??????????????????????*) *channel/UC??????????????????????*)
printf ' Found!\n'
line=${line##*channel/} line=${line##*channel/}
line=${line%%\"*} line=${line%%\"*}
printf "[%s/%s] using channel ID '%s' for xml link\n" "$count" "$total" "$line" printf "[%s/%s] using channel ID '%s' for xml link\n" "$count" "$total" "$line"
@@ -76,18 +102,10 @@ gen_links() {
"$line" >>"$SUBS_LINKS" "$line" >>"$SUBS_LINKS"
break break
esac esac
done & done
sleep "${SUBS_SLEEP_VALUE:-0}"
esac esac
done <"$SUBS_FILE" done <"$SUBS_FILE"
count=0
while [ "$count" -ne "$total" ] ; do
count=$(wc -l < "$SUBS_LINKS")
printf "[%s/%s] waiting for jobs to complete...\n" "$count" "$total"
sleep 0.5
done
} }
# -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* # -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
@@ -97,7 +115,13 @@ gen_links() {
# with a line of its videos dates, titles, and urls. # with a line of its videos dates, titles, and urls.
# -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* # -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
get_vids() { get_vids() {
data=$(curl -sfL --retry 15 "$1") data=$(curl -sL --retry 15 "$1")
if printf '%s\n' "$data" | grep '404 Not Found' >/dev/null ; then
>&2 printf '[ERROR]: Could not get vids for %s... 404\n' "$1"
>&2 printf '[%s] %s\n' "$(date)" "$1" >> "$SUBS"/ERRORS.log
return 1
fi
# hide the first <published> tag which is the channel # hide the first <published> tag which is the channel
# creation date # creation date
@@ -134,6 +158,7 @@ get_vids() {
# -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* # -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
# Updates the local cache of subscriptions. ([-u] flag) # Updates the local cache of subscriptions. ([-u] flag)
# shellcheck disable=2086
# -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* # -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
update_subs() { update_subs() {
[ -f "$SUBS_LINKS" ] || die 'Subs links have not been generated.' [ -f "$SUBS_LINKS" ] || die 'Subs links have not been generated.'
@@ -142,6 +167,7 @@ update_subs() {
mkdir -p "$SUBS_CACHE" mkdir -p "$SUBS_CACHE"
total=$(wc -l <"$SUBS_LINKS") total=$(wc -l <"$SUBS_LINKS")
total=${total##* }
count=0 count=0
while read -r link ; do while read -r link ; do
@@ -152,10 +178,13 @@ update_subs() {
done <"$SUBS_LINKS" done <"$SUBS_LINKS"
count=0 count=0
while [ "$count" -ne "$total" ] ; do max_retries=$total
while [ "$count" -ne "$total" ] && [ "$max_retries" -ne "$total" ] ; do
count=$(printf '%s\n' "$SUBS_CACHE"/* | wc -l) count=$(printf '%s\n' "$SUBS_CACHE"/* | wc -l)
count=${count##* }
printf "[%s/%s] waiting for fetch jobs to complete...\n" "$count" "$total" printf "[%s/%s] waiting for fetch jobs to complete...\n" "$count" "$total"
sleep 0.5 sleep 1
max_retries=$(( max_retries + 1 ))
done done
printf '%s\n\n' 'done!' printf '%s\n\n' 'done!'
@@ -182,25 +211,31 @@ cat_subs() {
# Finally, play the result with mpv. # Finally, play the result with mpv.
get_sel() { get_sel() {
if [ -d "$SUBS_CACHE" ] ; then if [ -d "$SUBS_CACHE" ] ; then
sel=$(cat_subs | $SUBS_MENU_PROG) # Pipe your subs feed into your desired menu and replace HTML escape codes (&quot; -> ")
sel=$(cat_subs | sed -e 's/&quot;/"/g' -e 's/&amp;/\&/g' -e 's/&lt;/</g' -e 's/&gt;/>/g' | $SUBS_MENU_PROG)
else else
die 'Subs cache has not been retrieved.' die 'Subs cache has not been retrieved.'
fi fi
[ "$sel" ] || die Interrupted [ "$sel" ] || die Interrupted
chan="${sel#* }" oldchan="${sel#* }"
chan="${chan%%] *}" chan=$(printf '%s' "${oldchan%%] *}" | sed -e 's/\&/&amp;/g' -e 's/"/\&quot;/g' -e 's/</\&lt;/g' -e 's/>/\&gt;/g' )
title=${sel#*"$chan"\] } title=$(printf '%s' "${sel#*"${oldchan%%] *}"\] }" | sed -e 's/\&/&amp;/g' -e 's/"/\&quot;/g' -e 's/</\&lt;/g' -e 's/>/\&gt;/g' )
while read -r line ; do while read -r line ; do
case $line in case $line in
*"$SEP$title$SEP"*) *"$SEP$title$SEP"*)
url=${line##*$SEP} url=${line##*$SEP}
if [ "$url" ] ; then if [ "$url" ] ; then
printf 'playing: %s\n' "$url" if [ "$DONT_PLAY" ] ; then
# Play the selection. # just print the url
# shellcheck disable=2086 # (useful for piping to other programs)
exec mpv $MPV_OPTS "$url" printf '%s\n' "$url"
else
printf 'playing: %s\n' "$url"
# shellcheck disable=2086
exec mpv $MPV_OPTS "$url"
fi
fi fi
break break
esac esac
@@ -213,6 +248,7 @@ daemonize() {
daemon_file=${XDG_CACHE_HOME:-~/.cache}/subs_daemon.cache daemon_file=${XDG_CACHE_HOME:-~/.cache}/subs_daemon.cache
if [ ! -f "$daemon_file" ] ; then if [ ! -f "$daemon_file" ] ; then
cp -f "${SUBS_FILE:=~/files/subs.txt}" "$daemon_file" cp -f "${SUBS_FILE:=~/files/subs.txt}" "$daemon_file"
gen_links
fi fi
while true ; do while true ; do
@@ -221,29 +257,42 @@ daemonize() {
cp -f "${SUBS_FILE:=~/files/subs.txt}" "$daemon_file" cp -f "${SUBS_FILE:=~/files/subs.txt}" "$daemon_file"
fi fi
update_subs update_subs
interval=${SUBS_DAEMON_INTERVAL:-$(( 5 * 60 ))} printf 'Sleeping for %s seconds...\n' "$SUBS_DAEMON_INTERVAL"
printf 'Sleeping for %s seconds...\n' "$interval" sleep "$SUBS_DAEMON_INTERVAL"
sleep "$interval"
done done
} }
main() { main() {
mkdir -p "$SUBS" mkdir -p "$SUBS"
case ${1#-} in if [ "$1" ] ; then
m) case ${1#-} in
export MPV_OPTS="$MPV_OPTS --no-video" g)
shift gen_links
esac ;;
u)
case ${1#-} in update_subs
h) usage ;; ;;
g) gen_links ;; c)
u) update_subs ;; cat_subs
c) cat_subs ;; ;;
d) daemonize ;; d)
*) get_sel daemonize
esac ;;
m)
MPV_OPTS="$MPV_OPTS --no-video" \
get_sel
;;
n)
DONT_PLAY=true \
get_sel
;;
*)
usage
esac
else
get_sel
fi
} }
main "$@" main "$@"