Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The Ultimate Way of Browsing Channels #1320

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
72 changes: 72 additions & 0 deletions .local/bin/channelrefresh
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/bin/dash

while ! ping -c 1 9.9.9.9 > /dev/null 2>&1; do sleep 0.5; done

INSTALLER="sudo pacman -S --noconfirm"
DATA_DIR="$HOME/.cache/youtube_channels"
CHANNEL_LIST="$HOME/.local/share/channels.txt"
mkdir -p "$DATA_DIR" && touch "$CHANNEL_LIST"

error_handling() {
[ -s "$CHANNEL_LIST" ] || {
notify-send "You don't have any channels in 'channels.txt'."
exit 1
}
grep -q "^.*=https://www.youtube.com/@[[:alnum:]]*/videos$" "$CHANNEL_LIST" || {
notify-send "'channels.txt' formatting is wrong."
exit 1
}
for pkg in yt-dlp mpv jq; do
command -v "$pkg" >/dev/null || {
notify-send "$pkg is not installed. Installing..."
$INSTALLER "$pkg" || {
emrakyz marked this conversation as resolved.
Show resolved Hide resolved
notify-send "Failed to install $pkg."
exit 1
}
}
done
}

compare_data() {
local channel_name="$1"
local data_file="${DATA_DIR}/${channel_name}.tsv"
local old_data_file="${DATA_DIR}/${channel_name}_old.tsv"

[ -e "$old_data_file" ] && {
old_urls=$(cut -f2 "$old_data_file")
new_urls=$(cut -f2 "$data_file")

echo "$old_urls" | sort > temp1
echo "$new_urls" | sort > temp2
new_videos=$(comm -13 temp1 temp2 | wc -l)
rm temp1 temp2

[ "$new_videos" -gt 0 ] && notify-send -u critical "$channel_name | $new_videos videos"
}
}

update_data() {
local channel_name="$1"
local channel_url="$2"
local data_file="${DATA_DIR}/${channel_name}.tsv"
local old_data_file="${DATA_DIR}/${channel_name}_old.tsv"

mv "$data_file" "$old_data_file" 2>/dev/null

yt-dlp -j --flat-playlist --skip-download --extractor-args youtubetab:approximate_date "$channel_url" | jq -r '[.title, .url, .view_count, .duration, .upload_date] | @tsv' > "$data_file"
}

update_all_channels() {
while IFS="=" read -r channel_name channel_url; do
update_data "$channel_name" "$channel_url" &
done < "$CHANNEL_LIST"

wait

while IFS="=" read -r channel_name channel_url; do
compare_data "$channel_name"
done < "$CHANNEL_LIST"
}

error_handling
update_all_channels
257 changes: 257 additions & 0 deletions .local/bin/yt-browser
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
#!/bin/dash

DATA_DIR="$HOME/.cache/youtube_channels"
DOWNLOAD_DIR="$HOME/ytvideos"
CATEGORY_LIST="$HOME/.local/share/categories.txt"
CHANNEL_LIST="$HOME/.local/share/channels.txt"
CUSTOM_LIST_DIR="$DATA_DIR/custom_lists"

mkdir -p "$DATA_DIR" "$DOWNLOAD_DIR" "$CUSTOM_LIST_DIR"

DMENU() {
dmenu -i -l $1 -p "$2"
}

sort_videos() {
data_file="$1"
sort_option="$2"

case $sort_option in
"@@sv") sort -nr -t" " -k3 "$data_file" ;;
"@@sd") sort -nr -t" " -k4 "$data_file" ;;
*) sort -nr -t" " -k5 "$data_file" ;;
esac | cut -f1
}

get_videos() {
channel_name="$1"
sort_option="$2"
data_file="$DATA_DIR/$channel_name.tsv"
sort_videos "$data_file" "$sort_option"
}

video_url() {
channel_name="$1"
video_title="$2"
data_file="$DATA_DIR/$channel_name.tsv"
sed -n "s/$video_title\t\([^\t]*\)\t.*$/\1/p" "$data_file"
}

rofi_action() {
echo "WATCH\nDOWNLOAD\nSEND TO A LIST" | DMENU 3 "Choose an action for the video"
}

rofi_custom_list_action() {
echo "$(find "$CUSTOM_LIST_DIR" -maxdepth 1 -type f -exec basename {} \;)
#### CREATE A LIST ####
#### DELETE A LIST ####" | DMENU 10 "Choose an action or list"
}

list_video_action() {
echo "WATCH\nDOWNLOAD\nDELETE" | DMENU 3 "Choose an action for the video"
}

add_to_list() {
video_title="$1"
channel_name="$2"
list_name="$3"
echo "$channel_name: $video_title" >> "$CUSTOM_LIST_DIR/$list_name"
}

custom_list_menu() {
while true; do
list="$(rofi_custom_list_action)"
[ -z "$list" ] && return
case "$list" in
*CREATE*)
new_list=$(DMENU 0 "Enter the name of the new list")
[ -n "$new_list" ] && touch "$CUSTOM_LIST_DIR/$new_list"
;;
*DELETE*)
delete_list=$(find "$CUSTOM_LIST_DIR" -maxdepth 1 -type f -exec basename {} \; |
DMENU 10 "Choose a list to delete")
[ -n "$delete_list" ] && rm "$CUSTOM_LIST_DIR/$delete_list"
;;
*)
custom_list_video_menu "$list"
;;
esac
done
}

custom_list_video_menu() {
list_name="$1"
while true; do
video_info=$(cat "$CUSTOM_LIST_DIR/$list_name" | DMENU 20 "Choose a video")
[ -z "$video_info" ] && return
channel_name="${video_info%%: *}"
video_title="${video_info##*: }"
custom_list_video_action_menu "$video_title" "$channel_name" "$list_name"
done
}

custom_list_video_action_menu() {
video_title="$1"
channel_name="$2"
list_name="$3"

action=$(list_video_action)

case $action in
WATCH)
video_process WATCH "$video_title" "$channel_name"
;;
DOWNLOAD)
video_process DOWNLOAD "$video_title" "$channel_name" && notify-send "Downloading has finished."
;;
DELETE)
escaped_video_title="$(echo "$video_title" | sed 's/[][\^$.\/|*+?(){}#]/\\&/g')"
sed "/$escaped_video_title/d" "$CUSTOM_LIST_DIR/$list_name" > "$CUSTOM_LIST_DIR/${list_name}.tmp" &&
mv "$CUSTOM_LIST_DIR/${list_name}.tmp" "$CUSTOM_LIST_DIR/$list_name"
;;
*)
return
;;
esac
}

video_process() {
action="$1"
video_title="$2"
channel_name="$3"
video_url=$(video_url "$channel_name" "$video_title")

case "$action" in
WATCH)
mpv "$video_url"
;;
DOWNLOAD)
local channel_download_dir="$DOWNLOAD_DIR/$channel_name"
mkdir -p "$channel_download_dir"
yt-dlp -o "$channel_download_dir/%(title)s.%(ext)s" "$video_url"
notify-send "Downloading has finished."
;;
esac
}

get_all_videos() {
sort_option="$1"
all_videos_file="$DATA_DIR/all_videos.tsv"

rm -f "$all_videos_file"
while IFS= read -r line
do
channel_name="${line%%=*}"
cat "$DATA_DIR/$channel_name.tsv" >> "$all_videos_file"
done < "$CHANNEL_LIST"

sort_videos "$all_videos_file" "$sort_option"
}

browse_all_channels() {
while true; do
video_title=$(get_all_videos | DMENU 20 "Choose a video or enter @@sv or @@sd")

[ -z "$video_title" ] && break || {
[ "$video_title" = "@@sv" -o "$video_title" = "@@sd" ] &&
sort_option="$video_title" && video_title=$(get_all_videos "$sort_option" | DMENU 20 "Choose a video")
}

[ -n "$video_title" -a "$video_title" != "@@sv" -a "$video_title" != "@@sd" ] && {
while read -r line; do
channel_name="${line%%=*}"
grep -qF "$video_title" "${DATA_DIR}/${channel_name}.tsv" && break
done < "$CHANNEL_LIST"

video_action_menu "$video_title" "$channel_name"
}
done
}

category_menu() {
while true; do
category=$(echo "$(cut -d= -f1 "$CATEGORY_LIST")" | DMENU 12 "Choose a category")
[ -z "$category" ] && return

channel_menu "$category"
done
}

channel_menu() {
category="$1"
IFS="|"

channels=$(sed -n "s/^${category}=\(.*\)$/\1/p" "$CATEGORY_LIST")
set -- $channels

while true; do
channel_name=$(printf '%s\n' "$@" | DMENU 20 "Choose a channel")
[ -z "$channel_name" ] && return

video_menu "$channel_name"
done
}

video_menu() {
channel_name="$1"

while true; do
video_title=$(get_videos "$channel_name" | DMENU 20 "Choose a video")
[ -z "$video_title" ] && return
[ "$video_title" = "@@sv" -o "$video_title" = "@@sd" ] && {
sort_option="$video_title"
video_title=$(get_videos "$channel_name" "$sort_option" | DMENU 20 "Choose a video")
}

video_action_menu "$video_title" "$channel_name"
done
}

video_action_menu() {
video_title="$1"
channel_name="$2"

while [ -n "$video_title" ] && [ "$video_title" != "@@sv" ] && [ "$video_title" != "@@sd" ]; do
action=$(rofi_action)

case $action in
WATCH)
video_process WATCH "$video_title" "$channel_name"
;;
DOWNLOAD)
video_process DOWNLOAD "$video_title" "$channel_name" && notify-send "Downloading has finished."
;;
"SEND TO A LIST")
list_name=$(find "$CUSTOM_LIST_DIR" -maxdepth 1 -type f -exec basename {} \; | DMENU 10 "Choose a list")
[ -n "$list_name" ] && add_to_list "$video_title" "$channel_name" "$list_name" &&
notify-send ""$video_title" is added to the list: "$list_name""
;;
*)
return
;;
esac
done
}

main_menu() {
options=" #### ALL CHANNELS ####
#### CATEGORIES ####
#### CUSTOM LISTS ####
$(cut -d= -f1 "$CHANNEL_LIST")"

echo "$options" | DMENU 20 "Choose an Option or a Category"
}

while true; do

main_choice=$(main_menu)

[ -z "$main_choice" ] && exit

case "$main_choice" in
*ALL\ CHANNELS*) browse_all_channels ;;
*CATEGORIES* ) category_menu ;;
*CUSTOM\ LISTS*) custom_list_menu ;;
*) video_menu "$main_choice" ;;
esac
done