123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262 |
- #!/bin/bash
- usage() {
- if [[ "$@" != "" ]]; then
- echo_term "$@"
- notify "$@" "Help text in terminal: $me -h"
- fi
- echo_term "
- Play videos from URLs with mpv+yt-dlp (youtube-dl). Example:
- mpv-clip \"\$(xclip -o)\"
- Will copy the URL back to the clipboard or primary selection, formatted with
- media title.
- Currently yt-dlp is recommend instead of youtube-dl (can be configured in
- ~/.config/mpv/mpv.conf with 'script-opts=ytdl_hook-ytdl_path=yt-dlp').
- Without any youtube-dl compatible, mpv can only deal with direct media URLs.
- Notifications are sent by default, using $me for both app and icon name.
- Options:
- -a Play only audio, but open a small controllable window with elapsed/total
- time, and don't disable the screensaver.
- -p Advance in playlist - does not seem to work with youtube currently.
- Does work with e.g. bandcamp.
- -m int Define desired maximum height of video (not always available).
- The best available audio is always chosen!
- Please keep in mind that youtube-dl always defaults to the best
- available quality unless specified. Overrides -f.
- -f int youtube-dl format. Example: 22. No default. Overrides -m.
- -y Replaces youtube with invidious instance (dynamically fetched from
- $ivio)
- Fallback: $ytmirror_fallback
- Requires ${ytmirrordeps[@]}.
- -r str Comma-separated list of prefixes to remove from URL
- -s str Override list of subtitle languages to embed. The default is to use
- your locale + English (2 letter ISO 639-1 codes): \"${slang}\". Pass empty
- string \"\" for ALL subtitles; can be a lot for e.g. youtube!
- -c str How to copy URL+title back to the clipboard - this string must contain
- exactly two letters:
- 1. c|p|b for clipboard, primary selection, or both
- 2. b|m|t for BBCode, markdown or plain text
- Default: $clipboard$markup.
- -t int Network timeout in seconds.
- -N Do NOT send desktop notifications.
- -h This text.
- "
- exit 1
- }
- notify() {
- [[ "$notify" == 1 ]] && notify-send -a "$me" -i "$me" -- "$@" || \
- echo_term "$@"
- }
- echo_term() {
- [ -t 1 ] && echo -e "$@"
- }
- clipboard() {
- [[ "$clipboard" == "" ]] && return
- # variables URL and title must be defined
- case $markup in
- b) out="[url=${URL}]${title}[/url]"
- ;;
- m) out="[${title}](${URL})"
- ;;
- t) out="$URL --- $title"
- ;;
- esac
- case $clipboard in
- c) echo "$out" | xsel -b
- ;;
- p) echo "$out" | xsel -p
- ;;
- b) echo "$out" | xsel -b
- echo "$out" | xsel -p
- ;;
- esac
- }
- me="${0##*/}" # xprop class name to be used by xdotool and optionally window manager
- bbcode=0 # fill clipboard with BBcode instead of plain text
- notify=1
- timeout=5 # network timeout in seconds
- geometry="33%x33%+0-0"
- maxheight=''
- clipboard='c'
- cbtext="clipboard"
- markup='b'
- yt_format=''
- ivio="https://api.invidious.io/?sort_by=type,health"
- ytmirror_fallback="invidious.snopyta.org"
- ytmirror=0
- ytmirrordeps=(curl xmllint)
- prefixes=()
- for dep in mpv; do
- type "$dep" >/dev/null || usage
- done
- slang="${LANG%%_*}"
- slang="${slang,,}";
- [[ "$slang" == en ]] || slang="${slang},en"
- # Just to initialize the array...
- opts=( --force-window=yes ) # mpv options for all use cases
- while getopts apm:f:ys:r:c:t:i:hN opt; do
- case $opt in
- a) geometry="33%x30+0+0"
- opts=( ${opts[@]} --vid=no --sub=no
- --osd-scale-by-window=no --no-osd-bar --no-osc
- --osd-msg1=\${time-pos}/\${duration}
- --osd-font-size=28 --osd-font=monospace --background=.1/.1/.1
- --osd-color=.9/.9/.9 --osd-border-size=0
- --osd-margin-y=0 --osd-margin-x=5 --no-stop-screensaver )
- me="${me}-audioonly"
- ;;
- p) opts=( ${opts[@]} --ytdl-raw-options=ignore-errors=,yes-playlist= ) # as of 2020-10-19, this does not seem to work at all with youtube (or invidious mirrors)
- me="${me}-playlist"
- ;;
- m)
- [ "$OPTARG" -lt 144 ] || [ "$OPTARG" -gt 6912 ] && usage "-$opt: Invalid number: $OPTARG"
- maxheight="$OPTARG"
- yt_format=""
- ;;
- f)
- (( OPTARG >= 0 )) || usage "-$opt: Invalid number: $OPTARG"
- yt_format="$OPTARG"
- maxheight=""
- ;;
- y) ytmirror=1
- for dep in "${ytmirrordeps[@]}"; do type $dep >/dev/null || usage; done
- ;;
- s) slang="$OPTARG" # comma separated list of subtitle languages - pass empty string '' or "" to get ALL subtitles
- ;;
- r) # comma separated list of prefixes to remove from URL converted to array
- mapfile -d, -t prefixes <<<"$OPTARG"
- prefixes[-1]="${prefixes[-1]%$'\n'}" # remove last char, which is a newline
- ;;
- c) (( "${#OPTARG}" != 2 )) && usage "Invalid argument -$opt $OPTARG"
- clipboard="${OPTARG:0:1}"
- markup="${OPTARG:1:1}"
- [[ "$clipboard" =~ ^(c|p|b)$ ]] && [[ "$markup" =~ ^(b|m|t)$ ]] || usage "Invalid argument -$opt $OPTARG"
- type xsel >/dev/null || usage
- case $clipboard in
- c) cbtext="clipboard" ;;
- p) cbtext="primary selection" ;;
- b) cbtext="clipboard and primary selection" ;;
- esac
- ;;
- t) timeout="$OPTARG"
- [[ "$OPTARG" =~ ^[1-9][0-9]*$ ]] || usage "-$opt: Invalid number: $OPTARG (must be a positive integer)"
- ;;
- i) icon="$OPTARG"
- [ -r "$OPTARG" ] || usage "-$opt: Cannot read $OPTARG"
- ;;
- N) notify=0
- ;;
- h|*) usage
- ;;
- esac
- done
- shift $((OPTIND-1))
- cbtext="(copied URL+title to $cbtext)"
- # first things first...
- URL="$1"
- # remove leading & trailing whitespace:
- URL="${URL%[[:space:]]*}"
- URL="${URL#[[:space:]]*}"
- # Remove prefixes
- for pre in "${prefixes[@]}"; do
- echo removed "$pre"
- URL="${URL#$pre}"
- echo "$URL"
- done
- [[ "$URL" != http?(s)://?(www.)* ]] && usage "$URL is not a valid URL"
- # silently drop notifications if this is not available
- type notify-send >/dev/null 2>&1 || [[ "$notify" == 0 ]]
- if [[ "$ytmirror" == 1 ]]; then
- entry=1 # use first entry, or second, etc.
- # get the mirror
- ytmirror="$(curl "$ivio" | xmllint --html --xpath "//div[@class=\"content\"]/table/tbody/tr[$entry]/td[1]/a/@href" - )"
- ytmirror="${ytmirror##*https://}"
- ytmirror="${ytmirror%%\"*}"
- ytmirror="${ytmirror%/}" # if there is a trailing slash, remove it
- curl -I "$ytmirror" || ytmirror="$ytmirror_fallback"
- ytdomains=( www.youtube.com youtube.com m.youtube.com youtu.be )
- for domain in "${ytdomains[@]}"; do
- if [[ "$URL" == http?(s)://$domain/* ]]; then
- #~ if [[ "$ytmirror" != "" ]] && host -W"$timeout" "$ytmirror" && ping -c1 -n -w"$timeout" "$ytmirror"; then
- URL="${URL/$domain/$ytmirror}" && break
- #~ else
- #~ ytmirror=''
- #~ fi >/dev/null
- #~ break
- fi
- done
- fi
- # introducing raw options passed to youtube-dl
- [[ "$me" == *"-audioonly" ]] && ytdlrawopts="format=bestaudio" || {
- [[ "$maxheight" != "" ]] && ytdlrawopts="format=bestvideo[height<=$maxheight]+bestaudio/best"
- [[ "$yt_format" != "" ]] && ytdlrawopts="format=$yt_format"
- }
- [[ "$maxheight" != "" ]] && ytdlrawopts="${ytdlrawopts},"
- # we will restrict video resolution whether the user explicitely requests it or not
- ytdlrawopts="${ytdlrawopts}socket-timeout=$timeout"
- [[ "$slang" == "" ]] || ytdlrawopts="${ytdlrawopts},sub-lang=\"$slang\""
- # for indentifying windows (and notifications)
- me="$me-$(printf '%(%H:%M:%S)T')"
- # Building the command line
- opts=( ${opts[@]}
- --network-timeout=$timeout
- --geometry="$geometry"
- --ytdl --script-opts=try_ytdl_first=yes
- --ytdl-raw-options="$ytdlrawopts"
- --x11-name="$me"
- --quiet
- --no-msg-color
- --no-input-terminal --term-playing-msg='${media-title}'
- --no-resume-playback
- --watch-later-directory=/dev/null
- )
- notify "Trying: $URL" "$me"
- notify coproc mpv "${opts[@]}" "$URL"
- coproc mpv "${opts[@]}" "$URL"
- error=0
- while read -r -u "${COPROC[0]}" line; do
- echo "$line"
- if [[ "$line" == "term-msg: "* ]]; then
- title="${line#*: }"
- notify "$title" "$me $cbtext"
- clipboard
- #~ often there are http errors, but playback continues regardless. so let's drop it.
- #~ elif [[ "$line" == *"HTTP error"* ]] || [[ "$line" == *"ytdl_hook: ERROR:"* ]]; then
- elif [[ "$line" == *"ytdl_hook: ERROR:"* ]]; then
- line="${line%%; please report this issue*}"
- notify "$line" "$me" && error=1
- fi
- done
- [[ "$error" == 1 ]] && exit 1
- clipboard
- notify "That was: $title" "Exiting $me $cbtext"
|