Skip to content

Instantly share code, notes, and snippets.

@schauveau
Created June 17, 2021 20:34
Show Gist options
  • Save schauveau/c4519b341a23c6bad3c870b3761bc5d2 to your computer and use it in GitHub Desktop.
Save schauveau/c4519b341a23c6bad3c870b3761bc5d2 to your computer and use it in GitHub Desktop.
A bash script to play video URL on a remote Kodi server.
#!/bin/bash
#
# A small shell script that uses kodi-send and youtube-dl to play
# videos (or audio) URLs on a Kodi server.
#
# Examples:
# - send a Youtube page
# kodi-start-video.sh https://www.youtube.com/watch?v=Xwn8fQSW7-8
# - send a DailyMotion page
# kodi-start-video.sh https://www.dailymotion.com/video/x7syshi
# - send a MP4 file
# kodi-start-video.sh https://sample-videos.com/video123/mp4/480/big_buck_bunny_480p_30mb.mp4
# - send the URL in primary buffer
# kodi-start-video.sh primary
# - send the URL in clipboard buffer
# kodi-start-video.sh clipboard
#
# The script does not download the video. Youtube-dl is only used
# to extract the video stream URL from the web page.
#
#
# Dependencies:
#
# - kodi-send to send the commands to Kodi.
# - youtube-dl (https://youtube-dl.org/) to extract the URL of the video stream
# - wl-paste to read the selection (on Wayland).
# - xclip to read the selection (on X11).
# - Common shell utilities: grep, head, sed, ...
#
# On Debian: sudo apt install kodi-eventclients-kodi-send sed wl-clipboard
#
# TODO:
# - Append to current playlist. How? The "Cast Kodi" Firefox extension can do it. JSON?
# - Add all videos in a playlist.
# - Use the Json interface instead of kodi-send? (see https://kodi.wiki/view/JSON-RPC_API)
#
VERB=yes
YOUTUBE_DL="youtube-dl"
# For Youtube URL only: Set to "yes" if you have the Youtube add-on
# installed on Kodi and you want to use it instead of using youtube-to
# to extract the video stream url.
WITH_YOUTUBE_ADDON="yes"
# Youtube-dl rules to select the video quality.
# See also FORMAT SELECTION in youtube-dl man page
# Remark: Using bestvideo+bestaudio is not a good idea since that would produce
# 2 separate video and audio streams and we cannot mux them together.
H=720 # Maximum Video Height for my Raspberry Pi
FORMAT="best[height<=$H]/bestvideo[height<=$H]/bestaudio"
SERVER=kodi.schauveau.local
PORT=9777
usage () {
APP=$(basename $0)
cat <<EOF
Usage: $APP [OPTION]... [ URL | clipboard | primary ]
Start playing a video URL on a Kodi server.
-k KODI Set the Kodi server (currently $SERVER)
-p PORT Set the Kodi port (currently $PORT)
-d Send the URL directly to Kodi (mostly for debugging)
If the special values "clipboard" and "primary" are used then the
URL is obtained from the 1st word of the clipboard or primary
selection.
EOF
exit 0
}
read-primary () {
if [[ -n "$WAYLAND_DISPLAY" ]] ; then
wl-paste --primary --type text/plain
elif [[ -n "$DISPLAY" ]] ; then
# Not really tested
xclip -selection primary -o -t TEXT
else
echo "Sorry! Cannot read the primary selection"
fi
}
read-clipboard () {
if [[ -n "$WAYLAND_DISPLAY" ]] ; then
wl-paste --type text/plain
elif [[ -n "$DISPLAY" ]] ; then
# Not really tested
xclip -selection clipboard -o -t TEXT
else
echo "Sorry! Cannot read the clipboard"
fi
}
#
# Extract the first non-blank word from the input.
#
keep-first-word () {
grep -E '[[:alnum:]]' | head -n 1 | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]].*//'
}
send-command () {
KODI=( kodi-send --host "$SERVER" --port "$PORT" )
CMD=( "${KODI[@]}" --action="$1" )
if [[ $VERB == "yes" ]] ; then
echo -n "#" ; printf " %q" "${CMD[@]}" ; echo
fi
"${CMD[@]}"
}
get-stream-from-url () {
"$YOUTUBE_DL" -f "$FORMAT" -g "$URL"
}
start-youtube () {
echo "Starting Youtube video $1"
send-command "PlayMedia(plugin://plugin.video.youtube/?action=play_video&videoid=$1)"
}
SRC=arg
DIRECT=no
while getopts ":dcpk:" option; do
case "${option}" in
k) SERVER="$OPTARG" ;;
p) PORT="$OPTARG" ;;
d) DIRECT=yes ;;
\?) usage ;;
esac
done
shift $((OPTIND-1))
case "$1" in
clipboard)
URL="$( read-clipboard | keep-first-word )"
echo "Using 1st word of clipboard as URL ($URL)"
;;
primary)
URL="$( read-primary | keep-first-word )"
echo "Using 1st word of clipboard as URL ($URL)"
;;
*)
URL="$1"
;;
esac
if [[ -z "$URL" ]] ; then
echo "Error: No URL found"
exit 1
fi
if [[ "$DIRECT" == "no" ]] ; then
if [[ "$WITH_YOUTUBE_ADDON" == "yes" ]] ; then
YT_ID="[-a-zA-Z0-9_]{11}" # A Youtube ID is exactly 11 characters in a-z,A-Z,0-9,- and _
if [[ "$URL" =~ ^https://www.youtube.com/watch\?(.*&)*v=($YT_ID) ]] ; then
# A long Youtube URL such as https://www.youtube.com/watch?v=DXUAyRRkI6k
start-youtube "${BASH_REMATCH[2]}"
exit 0
elif [[ "$URL" =~ ^https://youtu.be/($YT_ID) ]] ; then
# A short Youtube URL such as https://youtu.be/DXUAyRRkI6k
start-youtube "${BASH_REMATCH[1]}"
exit 0
fi
fi
if [[ "$URL" =~ ^https?://.*\.(mp3|m3u|mp4|ogg|mpeg|avi|webm)$ ]] ; then
# Recognize some common video or audio extensions.
DIRECT=yes
elif [[ "$URL" =~ ^https?:// ]] ; then
# For other URLs, obtain the video stream using youtube-dl
# That works ... most of the time.
STREAM=$(get-stream-from-url "$URL" | head -n 1)
if [[ -n "$STREAM" ]] ; then
send-command "PlayMedia($STREAM)"
else
echo "Error: Failed to extract a video stream from URL."
echo " Falling back to direct play..."
DIRECT=yes
fi
else
DIRECT=yes
fi
fi
if [[ "$DIRECT" = "yes" ]] ; then
send-command "PlayMedia($URL)"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment