├── resources ├── seen.lst └── subscribes ├── screen.png ├── Makefile ├── README.md └── subtube /resources/seen.lst: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/subscribes: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nagy135/subtube/HEAD/screen.png -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .POSIX: 2 | 3 | PREFIX = /usr 4 | install: 5 | mkdir -p $(DESTDIR)$(PREFIX)/bin 6 | cp subtube $(DESTDIR)$(PREFIX)/bin/subtube 7 | chmod +x $(DESTDIR)$(PREFIX)/bin/subtube 8 | mkdir -p $(DESTDIR)$(PREFIX)/share/subtube/resources 9 | chmod 777 $(DESTDIR)$(PREFIX)/share/subtube/resources 10 | 11 | uninstall: 12 | rm $(DESTDIR)$(PREFIX)/bin/subtube 13 | rm -rf $(DESTDIR)$(PREFIX)/share/subtube 14 | 15 | .PHONY: install uninstall 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SUBTUBE 2 | 3 | ![](https://tokei.rs/b1/github/nagy135/subtube?category=code) 4 | 5 | ![sxiv picker](screen.png) 6 | 7 | Simple script that lets you watch videos from your subscribed channels without distraction of youtube page. 8 | 9 | **Table of Contents** 10 | 11 | - [SUBTUBE](#subtube) 12 | - [Explanation](#explanation) 13 | - [DEPENDENCIES](#dependencies) 14 | - [Optional change](#optional-change) 15 | - [ARCH](#arch) 16 | - [DEBIAN/UBUNTU](#debianubuntu) 17 | - [UPDATE](#update) 18 | - [INSTALL](#install) 19 | - [UNINSTALL](#uninstall) 20 | - [USAGE](#usage) 21 | - [COMMANDS](#commands) 22 | - [init](#init) 23 | - [update](#update) 24 | - [play](#play) 25 | - [add](#add) 26 | - [clean](#clean) 27 | - [newest](#newest) 28 | - [MPV_HISTORY](#mpv_history) 29 | - [SXIV integration](#sxiv-integration) 30 | - [CRONTAB](#crontab) 31 | - [BSPWM](#bspwm) 32 | - [WAYLAND](#wayland) 33 | - [FUTURE WORK](#future-work) 34 | 35 | 36 | # Explanation 37 | Whole idea came from realizing that [sxiv](https://github.com/muennich/sxiv) in it's thumbnail mode allows 38 | you to select images the same way programs like rofi, dmenu, fzf, etc. 39 | This makes it usefull as some sort of dialog. 40 | Images contain *video id* in it's name so all you need is to play it via [mpv](https://github.com/mpv-player/mpv) 41 | through [yt-dlp](https://github.com/yt-dlp/yt-dlp). 42 | Screenshot above has my modified fork of [sxiv](https://github.com/muennich/sxiv) so it looks little different. 43 | 44 | I rewrote this script with a lot of comments so feel free to fork and modify it to your liking 45 | 46 | # DEPENDENCIES 47 | 48 | This project has 3 dependencies and 2 optional ones. If you use arch or almost any normal linux distribution, this should be easy to get for you. 49 | 50 | * [sxiv](https://github.com/muennich/sxiv) or [nsxiv](https://github.com/nsxiv/nsxiv) 51 | * [yt-dlp](https://github.com/yt-dlp/yt-dlp) 52 | * [mpv](https://github.com/mpv-player/mpv) 53 | * === 54 | * [dunst](https://github.com/dunst-project/dunst) 55 | * [xob](https://github.com/florentc/xob) 56 | 57 | First 4 are core, last 3 are optional. 58 | **Dunst** is simply notification daemon that is heavily used. If you dont install dunst, you still get some notification (because you probably have different one), but dunst supports images and stacking. 59 | Without this dependency you wouldnt see any notifications, but it would still work. If you dont know what *notification daemon* means, you probably have it. 60 | **XOB** is progress bar, if it's not installed then progress bar will be automatically disabled. Using `subtube update --secret` also disables the progress bar. 61 | 62 | Script was rewritten to avoid bash dependency. 63 | Use any POSIX compliant shell to run (like *dash* but *bash* will also suffice). 64 | 65 | ## ARCH 66 | on arch based distro, you can skip getting dependencies and install via [AUR](https://aur.archlinux.org/packages/subtube-git/) in [install section](#install). 67 | ``` 68 | sudo pacman -S sxiv mpv dunst yt-dlp 69 | paru xob 70 | ``` 71 | 72 | ## DEBIAN/UBUNTU 73 | 74 | ``` 75 | pip install --user yt-dlp 76 | sudo apt install sxiv mpv dunst 77 | ``` 78 | Xob is not present on debian repo, install from source via link in *dependencies*. You will probably just need to run these commands 79 | 80 | ``` 81 | cd /tmp # or anywhere else if you wish to preserve repository 82 | git clone https://github.com/florentc/xob 83 | cd xob 84 | make 85 | sudo make install 86 | ``` 87 | 88 | # UPDATE 89 | if script stops working one day for you, you need to update your yt-dlp 90 | ``` 91 | pip install --user --update yt-dlp 92 | ``` 93 | 94 | 95 | # INSTALL 96 | 97 | Arch users can install from [AUR](https://aur.archlinux.org/packages/subtube-git/) with any [AUR helper](https://wiki.archlinux.org/title/AUR_helpers) (i'm using `paru`) 98 | ``` 99 | paru subtube-git 100 | ``` 101 | 102 | Or clone repository and install manually 103 | 104 | ``` 105 | git clone https://github.com/nagy135/subtube 106 | cd subtube 107 | sudo make install 108 | ``` 109 | These commands will clone repository and install executable to **/usr/bin**. 110 | 111 | ## UNINSTALL 112 | removes all files created 113 | ``` 114 | sudo make uninstall 115 | ``` 116 | 117 | # USAGE 118 | 119 | provides CLI interface via subtube (you might wanna check `subtube --help`) 120 | ``` 121 | subtube command 122 | ``` 123 | if ran without command, help is shown. 124 | The optimal workflow you should try is to bind commands to key presses in key handler of your choice. I prefer this configuration: 125 | 126 | Key press | command 127 | --- | --- 128 | super+y | `subtube play` 129 | super+F5 | `subtube update` 130 | 131 | ## COMMANDS 132 | 133 | ### init 134 | performs update but doesnt actually download any image. This is to fill your *seen database*. 135 | You can skip this and run *update* instead. 136 | 137 | ``` 138 | subtube init 139 | ``` 140 | 141 | ### update 142 | reads subscribe list, downloads the newest 10 videos (if not seen yet) and allows you to play them (with play) 143 | ``` 144 | subtube update 145 | ``` 146 | or 147 | ``` 148 | subtube update --secret 149 | subtube update -s 150 | ``` 151 | to avoid notification (I use it with [crontab 30 min interval](#crontab)) 152 | 153 | ### play 154 | brings up sxiv thumbnail selection, where (default sxiv bindings) **m** marks thumbnail and **q** closes it and starts playing all of marked videos (if any). 155 | ``` 156 | subtube play 157 | ``` 158 | or 159 | ``` 160 | subtube play --browser 161 | subtube play -b 162 | ``` 163 | to play with browser instead. 164 | Either specify with env variable (from env setting config like `~/.zshenv`) or manually like 165 | ``` 166 | BROWSER=google-chrome-stable subtube play -b 167 | ``` 168 | or let script try few popular options in random order 169 | 170 | OR 171 | ``` 172 | subtube play --handler "google-chrome-stable --incognito" 173 | subtube play -h "firefox --headless" 174 | ``` 175 | to play with custom handler. Each handler call wont block execution (ran in background). 176 | 177 | ### add 178 | adds new subscribed channel. This is a link of a channel's page. 179 | ``` 180 | subtube add "https://www.youtube.com/@3blue1brown" 181 | ``` 182 | 183 | ### clean 184 | gives you option to find and "mark to remove" old videos, where N means **at least N days old videos**. This brings sxiv selection to mark videos for deletion 185 | ``` 186 | subtube clean N 187 | ``` 188 | 189 | ### newest 190 | allows you to only pick from videos N minutes old (date when *subtube update* downloaded image). Very useful when you see notification but have many unseen videos to find it in. 191 | ``` 192 | subtube newest N 193 | ``` 194 | 195 | 196 | note that we use url of videos folder, not channel. This is because we find new videos by parsing page html (link has to point to videos folder!) 197 | 198 | # MPV_HISTORY 199 | Another script of mine [mpv_history](https://github.com/nagy135/mpv_history) can be used to replay video again. 200 | Simply install it and subtube will populate it with records. 201 | 202 | # SXIV integration 203 | if you add following lines to your sxiv config (~/.config/sxiv/exec/key-handler) you will be able to show video title as notification or remove thumbnails. 204 | 205 | After sxiv installation, create config folder and key handler script like this 206 | ``` 207 | mkdir -p ~/.config/sxiv/exec 208 | touch ~/.config/sxiv/exec/key-handler 209 | ``` 210 | 211 | then add this content to the file (If this case statement already exists, so just copy 3 lines inside.) 212 | 213 | ``` 214 | #!/bin/bash 215 | 216 | case "$1" in 217 | "n") while read file; do subtube name $file & disown ; done ;; 218 | "l") while read file; do subtube name_length $file & disown ; done ;; 219 | "r") while read file; do rm $file; done ;; # remove thumbnail 220 | esac 221 | ``` 222 | then make it executable 223 | ``` 224 | chmod +x ~/.config/sxiv/exec/key-handler 225 | ``` 226 | 227 | this allows you to use sxiv prefix (ctrl+x) followed by key inside quotes to perform additional actions 228 | 229 | # CRONTAB 230 | put these lines to you crontab file to download new thumbnails every 10 minutes. 231 | There is little issue with this showing notification on some systems. 232 | You might have to google a little to make it work. 233 | But once your crontab can regularly spawn `notify send 'title' 'body'`, it will work and refresh new videos regularly. 234 | If you can't `notify send` from crontab, it would simply update without notification. 235 | ``` 236 | */10 * * * * XDG_RUNTIME_DIR=/run/user/$(id -u) subtube update --secret 237 | ``` 238 | 239 | # BSPWM 240 | i m using it on bspwm, so there is "one-shot sticky floating small middle screen" rule with notification if too many thumbnails to fit 241 | 242 | # WAYLAND 243 | My daily usage is currently on [wayland](https://wiki.archlinux.org/title/wayland). 244 | If you use [xwayland](https://wiki.archlinux.org/title/wayland#XWayland) you should be fine but on [wayland branch](https://github.com/nagy135/subtube/tree/wayland) there are some wayland related changes. We also have **wayland AUR package** in [install section](#install) 245 | 246 | # FUTURE WORK 247 | * make repo of my "mpv history" script, that can play already played videos via rofi launcher 248 | -------------------------------------------------------------------------------- /subtube: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | PREFIX=/usr/share/subtube/resources 4 | 5 | # list of seen video hashes 6 | SEEN="$PREFIX/seen.lst" 7 | 8 | # storage of subscribed urls 9 | SUBSCRIBES="$PREFIX/subscribes" 10 | 11 | # storage of video titles 12 | VIDEO_METADATA="$PREFIX/video_metadata" 13 | 14 | # location of thumbnails 15 | THUMBNAILS="$PREFIX/thumbnails" 16 | 17 | # pidfile (update in progress) 18 | PIDFILE=/tmp/subtube_update.pid 19 | 20 | # separates metadata blocks 21 | SEPARATOR="||" 22 | 23 | # variables for XOB (progress bar) 24 | fifo=/tmp/subtube.fifo 25 | progressbar_pid=-1 26 | 27 | ensure_local_storage(){ 28 | [ -f "$SUBSCRIBES" ] || touch "$SUBSCRIBES" 29 | [ -f "$VIDEO_METADATA" ] || touch "$VIDEO_METADATA" 30 | [ -f "$SEEN" ] || touch "$SEEN" 31 | [ -d "$THUMBNAILS" ] || mkdir "$THUMBNAILS" 32 | } 33 | 34 | check_dep(){ 35 | if command -v "$1" >/dev/null; then 36 | echo "$1" 37 | else 38 | echo ":" 39 | fi 40 | } 41 | 42 | noti="$(check_dep notify-send)" 43 | img_view="$(check_dep nsxiv)" 44 | [ "$img_view" = ":" ] && img_view="sxiv" 45 | 46 | die(){ 47 | [ -n "$1" ] && printf "%s\n" "$*" >&2 48 | exit 1 49 | } 50 | 51 | add_subscriber(){ 52 | echo "$1" >> "$SUBSCRIBES" 53 | } 54 | 55 | update(){ 56 | # internet verification 57 | if ! ping -q -c 1 'example.com' >/dev/null 2>&1; then 58 | $noti -t 4000 -i 'none' -u critical "Subtube" "No internet connection" 59 | exit 1 60 | fi 61 | 62 | # avoid running twice at the same time 63 | if [ -f $PIDFILE ]; then 64 | $noti -i 'none' -u critical -t 2000 "Subtube" "Update already in progress, wait..." 65 | exit 0 66 | else 67 | touch "$PIDFILE" 68 | fi 69 | 70 | # cleanup if terminated by user 71 | trap "rm $PIDFILE" INT TERM 72 | 73 | # initialize progress bar (xob) 74 | [ "$1" != "secret" ] && progress_bar -d 75 | 76 | # init loop variables 77 | sub_i=0 78 | subcount=$(wc -l < "$SUBSCRIBES") 79 | 80 | # loop over subscribed urls 81 | while read -r line; do 82 | sub_i=$((sub_i+1)) 83 | 84 | # notify progress 85 | [ "$1" != "secret" ] && progress_bar $sub_i "$subcount" 30 86 | 87 | channel_id="$(yt-dlp --playlist-items 0 -O playlist:channel_url "$line" \ 88 | | awk -F '/' '{print $5}' 2> /dev/null)" 89 | 90 | # get recent video hashes parsed from youtube channel RSS page 91 | vid_ids="$(curl -s "https://www.youtube.com/feeds/videos.xml?channel_id=$channel_id" \ 92 | | grep "" \ 93 | | sed "s/<[^>]*>//g" \ 94 | | awk '{$1=$1};1' 2> /dev/null)" 95 | 96 | # loop over hashes 97 | echo "$vid_ids" | while read -r vid_id; do 98 | 99 | # filter out already seen ones 100 | if grep -qe "$vid_id" "$SEEN"; then continue; fi 101 | 102 | # if problem occured during parsing 103 | [ -z "$vid_id" ] && echo "error parsing given url: $line" && continue 104 | 105 | [ "$1" != "init" ] && get_thumbnail "$vid_id" 106 | echo "$vid_id" >> $SEEN 107 | [ "$1" = "init" ] && continue 108 | 109 | # get video metadata 110 | url="https://www.youtube.com/watch?v=$vid_id" 111 | title="$(get_title $vid_id)" 112 | duration="$(get_duration $vid_id)" 113 | 114 | echo "$vid_id $SEPARATOR $duration $SEPARATOR $title" >> "$VIDEO_METADATA" 115 | 116 | $noti -i 'none' -t 3000 "$title" "$duration" -u critical -i "$THUMBNAILS/$vid_id.jpg" 117 | done 118 | 119 | 120 | done < "$SUBSCRIBES" 121 | 122 | # kill progress bar 123 | if [ "$1" != "secret" ]; then 124 | $noti -i 'none' -t 3000 "Subtube" "$(find "$THUMBNAILS" -type f | wc -l) unseen video(s)" 125 | progress_bar -k 126 | fi 127 | 128 | # update finished, remove update flag 129 | rm "$PIDFILE" 130 | } 131 | 132 | # 1st argument: thumbnails folder 133 | # 2st argument: play with browser|handler 134 | # 3st argument: handler 135 | play(){ 136 | # this parts serves as override of THUMBNAILS folder 137 | thumbnails="$THUMBNAILS" 138 | [ -n "$1" ] && thumbnails="$1" 139 | 140 | # bspwm users get floating sxiv centered in middle of the screen 141 | pidof -s 'bspwm' >/dev/null && bspc rule -a 'Sxiv' --one-shot layer=above sticky=on state=floating rectangle=800x500+560+250 142 | 143 | chosen=$("$img_view" -tbop "$thumbnails" || $noti -i 'none' -t 3000 "Subtube" "No videos to play") 144 | 145 | if [ -z "$chosen" ]; then 146 | [ -z "$1" ] \ 147 | && exit \ 148 | || return 149 | fi 150 | 151 | count=$(echo "$chosen" | wc -l) 152 | i=1 153 | echo "$chosen" | while read -r choice; do 154 | # first move it so that its 'removed' instantly 155 | file_name=$(basename "$choice") 156 | mv "$choice" "/tmp/$file_name" 157 | choice="/tmp/$file_name" 158 | 159 | # get video hash 160 | id=$(echo "$choice" | sed "s/.*\/// ; s/\.jpg//") 161 | if [ "$count" -eq 1 ]; then 162 | notify_name "Playing" "$id" "$choice" 163 | else 164 | notify_name "Playing $i/$count" "$id" "$choice" 165 | i=$((i+1)) 166 | fi 167 | rm "$choice" 168 | 169 | title=$(get_title $id) 170 | url="https://www.youtube.com/watch?v=$id" 171 | 172 | # integration with mpv_history, script of mine storing every seen video 173 | # https://github.com/nagy135/mpv_history 174 | HISTORY_SEPARATOR="##@@##@@##" 175 | MPV_HISTORY="$HOME/.local/share/mpv_history/history" 176 | [ -f "$MPV_HISTORY" ] && echo "$title $HISTORY_SEPARATOR $url" >> "$MPV_HISTORY" 177 | 178 | if [ "$2" = "browser" ]; then 179 | if [ -z "${BROWSER+x}" ]; then 180 | for browser in \ 181 | chromium \ 182 | chrome \ 183 | google-chrome-stable \ 184 | google-chrome-unstable \ 185 | google-chrome-beta \ 186 | firefox \ 187 | firefox-nightly 188 | do 189 | command -v $browser >/dev/null 2>&1 \ 190 | && echo "playing with $browser" \ 191 | && $browser "$url" \ 192 | && break 193 | done 194 | else 195 | $BROWSER "$url" 196 | fi 197 | elif [ "$2" = "handler" ]; then 198 | $3 "$url" & 199 | else 200 | # try to play it until it works (maximum 3 times) 201 | # mpv has youtube-dl hook and sometimes it works on second try. 202 | # if 3 times is not enough, you probably just need to update your 203 | # local youtube-dl (python -m pip install --upgrade youtube-dl) 204 | n=0 205 | until [ "$n" -ge 3 ] 206 | do 207 | [ "$n" -ge 1 ] && $noti -t 1000 "MPV" "retry $n" 208 | mpv "$url" >/dev/null 2>&1 && break 209 | n=$((n+1)) 210 | sleep 2 211 | done 212 | fi 213 | grep -v "^$id" "$VIDEO_METADATA" > /tmp/subtube_metadata \ 214 | && cat /tmp/subtube_metadata > "$VIDEO_METADATA" 215 | done 216 | } 217 | 218 | notify_name(){ 219 | title="$(get_title $2)" 220 | duration="$(get_duration $2)" 221 | $noti -i "$3" -t 4000 "$1" "$title\n$duration" 222 | } 223 | 224 | metadata(){ 225 | id=$(echo "$2" | sed "s/.*\/// ; s/\.jpg//") 226 | title="$(get_title $id)" 227 | 228 | [ "$1" = "name" ] && $noti -i "$2" "$title" && exit 0 229 | duration="$(get_duration $id)" 230 | $noti -t 3000 -i "$2" "$title" "$duration" 231 | } 232 | 233 | get_title(){ 234 | cache=$(check_cache_metadata "$1" title) 235 | [ "$cache" != "" ] \ 236 | && echo "$cache" \ 237 | || yt-dlp -e "https://www.youtube.com/watch?v=$1" 238 | } 239 | 240 | get_duration(){ 241 | cache=$(check_cache_metadata "$1" duration) 242 | [ "$cache" != "" ] \ 243 | && echo "$cache" \ 244 | || yt-dlp --get-duration "https://www.youtube.com/watch?v=$1" 245 | } 246 | 247 | check_cache_metadata(){ 248 | lines=$(cat "$VIDEO_METADATA" | grep "$1" | head -n 1) 249 | line_count=$(echo "$lines" | wc -l) 250 | [ ! $line_count -eq 1 ] && echo "" && return 251 | 252 | # parse id || time_length || title 253 | components=$(echo "$lines" \ 254 | | sed 's/^.\{11\}\s||\s\([0-9:]*\)\s||\s\(.*\)/\1\n\2/') 255 | 256 | [ $2 = "title" ] \ 257 | && echo "$components" | sed -n 2p 258 | 259 | [ $2 = "duration" ] \ 260 | && echo "$components" | sed -n 1p 261 | } 262 | 263 | # 1st argument switch [clean|newest] 264 | time_selection(){ 265 | sel_type=$1 266 | shift 267 | 268 | # make sure folder exists 269 | tmp_thumbnails="/tmp/tmp_thumbnails_$sel_type" 270 | mkdir -p "$tmp_thumbnails" 271 | 272 | # set find flags 273 | flag="-mtime" 274 | argument=+$1 275 | [ "$sel_type" = "newest" ] \ 276 | && flag="-mmin" \ 277 | && argument="-$1" 278 | 279 | # move all older than N days into the directory 280 | find "$THUMBNAILS" \ 281 | $flag "$argument" \ 282 | -type f \ 283 | -exec mv -t "$tmp_thumbnails" {} + 284 | 285 | # perform (n)sxiv selection 286 | if [ "$sel_type" = "newest" ]; then 287 | play "$tmp_thumbnails" 288 | else 289 | chosen=$("$img_view" -tbop "$tmp_thumbnails" || $noti -i 'none' -t 2000 "Subtube" "No video deleted") 290 | [ -n "$chosen" ] && rm "$chosen" 291 | fi 292 | 293 | # move rest back 294 | mv "$tmp_thumbnails"/* "$THUMBNAILS" 2> /dev/null 295 | 296 | # remove empty folder 297 | rmdir "$tmp_thumbnails" 298 | } 299 | 300 | get_thumbnail(){ 301 | wget -cq "https://i3.ytimg.com/vi/$1/hqdefault.jpg" >/dev/null 2>&1 -O "$THUMBNAILS/$1.jpg" 302 | } 303 | 304 | progress_bar(){ 305 | command -v xob >/dev/null || return 306 | if [ "$1" = '-d' ]; then 307 | mkfifo $fifo 308 | chmod 766 $fifo 309 | subcount=$(wc -l < "$SUBSCRIBES") 310 | tail -f $fifo | xob -t 5000 -s subtube -m "$subcount" >/dev/null 2>&1 & 311 | progressbar_pid=$! 312 | elif [ "$1" = '-k' ]; then 313 | rm $fifo 314 | kill $progressbar_pid 315 | pkill -f "tail -f $fifo" 316 | else 317 | echo "$1" > $fifo 318 | fi 319 | } 320 | 321 | ensure_local_storage 322 | 323 | if [ "$1" = 'update' ]; then 324 | arg="" 325 | if [ "$2" = "-s" ] || [ "$2" = "--secret" ]; then 326 | arg="secret" 327 | fi 328 | update $arg 329 | elif [ "$1" = 'init' ]; then 330 | update init 331 | elif [ "$1" = 'play' ]; then 332 | arg="" 333 | if [ "$2" = "-b" ] || [ "$2" = "--browser" ]; then 334 | arg="browser" 335 | fi 336 | if [ "$2" = "-h" ] || [ "$2" = "--handler" ]; then 337 | [ $# -lt 3 ] && die "you need to specify handler" 338 | play "" "handler" "$3" 339 | else 340 | play "" $arg 341 | fi 342 | elif [ "$1" = 'clean' ]; then 343 | [ $# -lt 2 ] && die "Pass number that specifies how many days" \ 344 | "old videos to keep (delete older)" 345 | time_selection clean "$2" 346 | elif [ "$1" = 'newest' ]; then 347 | [ $# -lt 2 ] && die "Pass number that specifies how many minutes" \ 348 | "old videos to show (for playing)" 349 | time_selection newest "$2" 350 | elif [ "$1" = 'add' ]; then 351 | [ $# -lt 2 ] && die "Pass url to videos page!" 352 | add_subscriber "$2" 353 | elif [ "$1" = 'name' ]; then 354 | [ $# -lt 2 ] && die "Pass path to file with video id!" 355 | metadata name "$2" 356 | elif [ "$1" = 'name_length' ]; then 357 | [ $# -lt 2 ] && die "Pass path to file with video id!" 358 | metadata name_length "$2" 359 | else 360 | tee <&2 361 | Usage: subtube [OPTION] 362 | Youtube interface without browser 363 | 364 | Options: 365 | -h, --help show this message and quit 366 | update update database 367 | --secret update database without notifications (for crontab) 368 | -s alias --secret 369 | play (n)sxiv picking dialog for playing all marked videos 370 | --browser play with your browser instead (guess or use \$BROWSER) 371 | -b alias --browser 372 | --handler [handler] play with custom handler (wrap in quotes)' 373 | -h [handler] alias --handler 374 | init dont download thumbnails - for first time 375 | name path_to_file notify-send video title 376 | name_length path_to_file notify-send video title (with length) 377 | clean N select videos older than N days to delete 378 | newest N select videos more recent than N minutes to choose from 379 | add [url] add subscriber (video page url of youtube channel) 380 | EOF 381 | fi 382 | --------------------------------------------------------------------------------