2016-10-11 10:37:35 +02:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
#
|
|
|
|
# Behaviour
|
|
|
|
# Userscript for qutebrowser which casts the url passed in $1 to the default
|
|
|
|
# ChromeCast device in the network using the program `castnow`
|
|
|
|
#
|
|
|
|
# Usage
|
|
|
|
# You can launch the script from qutebrowser as follows:
|
|
|
|
# spawn --userscript ${PATH_TO_FILE} {url}
|
|
|
|
#
|
|
|
|
# Then, you can control the chromecast by launching the simple command
|
|
|
|
# `castnow` in a shell which will connect to the running castnow instance.
|
|
|
|
#
|
|
|
|
# For stopping the script, issue the command `pkill -f castnow` which would
|
|
|
|
# then let the rest of the userscript execute for cleaning temporary file.
|
|
|
|
#
|
|
|
|
# Thanks
|
|
|
|
# This userscript borrows Thorsten Wißmann's javascript code from his `mpv`
|
|
|
|
# userscript.
|
|
|
|
#
|
|
|
|
# Dependencies
|
|
|
|
# - castnow, https://github.com/xat/castnow
|
|
|
|
#
|
|
|
|
# Author
|
|
|
|
# Simon Désaulniers <sim.desaulniers@gmail.com>
|
|
|
|
|
|
|
|
if [ -z "$QUTE_FIFO" ] ; then
|
|
|
|
cat 1>&2 <<EOF
|
|
|
|
Error: $0 can not be run as a standalone script.
|
|
|
|
|
|
|
|
It is a qutebrowser userscript. In order to use it, call it using
|
|
|
|
'spawn --userscript' as described in qute://help/userscripts.html
|
|
|
|
EOF
|
|
|
|
exit 1
|
|
|
|
fi
|
|
|
|
|
|
|
|
msg() {
|
|
|
|
local cmd="$1"
|
|
|
|
shift
|
|
|
|
local msg="$*"
|
|
|
|
if [ -z "$QUTE_FIFO" ] ; then
|
|
|
|
echo "$cmd: $msg" >&2
|
|
|
|
else
|
|
|
|
echo "message-$cmd '${msg//\'/\\\'}'" >> "$QUTE_FIFO"
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
|
|
|
js() {
|
|
|
|
cat <<EOF
|
|
|
|
|
|
|
|
function descendantOfTagName(child, ancestorTagName) {
|
|
|
|
// tells whether child has some (proper) ancestor
|
|
|
|
// with the tag name ancestorTagName
|
|
|
|
while (child.parentNode != null) {
|
|
|
|
child = child.parentNode;
|
|
|
|
if (typeof child.tagName === 'undefined') break;
|
|
|
|
if (child.tagName.toUpperCase() == ancestorTagName.toUpperCase()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
var App = {};
|
|
|
|
|
|
|
|
var all_videos = [];
|
|
|
|
all_videos.push.apply(all_videos, document.getElementsByTagName("video"));
|
|
|
|
all_videos.push.apply(all_videos, document.getElementsByTagName("object"));
|
|
|
|
all_videos.push.apply(all_videos, document.getElementsByTagName("embed"));
|
|
|
|
App.backup_videos = Array();
|
|
|
|
App.all_replacements = Array();
|
|
|
|
for (i = 0; i < all_videos.length; i++) {
|
|
|
|
var video = all_videos[i];
|
|
|
|
if (descendantOfTagName(video, "object")) {
|
|
|
|
// skip tags that are contained in an object, because we hide
|
|
|
|
// the object anyway.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
var replacement = document.createElement("div");
|
|
|
|
replacement.innerHTML = "
|
|
|
|
<p style=\\"margin-bottom: 0.5em\\">
|
|
|
|
The video is being cast on your ChromeCast device.
|
|
|
|
</p>
|
|
|
|
<p>
|
|
|
|
In order to restore this particular video
|
|
|
|
<a style=\\"font-weight: bold;
|
|
|
|
color: white;
|
|
|
|
background: transparent;
|
|
|
|
\\"
|
|
|
|
onClick=\\"restore_video(this, " + i + ");\\"
|
|
|
|
href=\\"javascript: restore_video(this, " + i + ")\\"
|
|
|
|
>click here</a>.
|
|
|
|
</p>
|
|
|
|
";
|
|
|
|
replacement.style.position = "relative";
|
|
|
|
replacement.style.zIndex = "100003000000";
|
|
|
|
replacement.style.fontSize = "1rem";
|
|
|
|
replacement.style.textAlign = "center";
|
|
|
|
replacement.style.verticalAlign = "middle";
|
|
|
|
replacement.style.height = "100%";
|
|
|
|
replacement.style.background = "#101010";
|
|
|
|
replacement.style.color = "white";
|
|
|
|
replacement.style.border = "4px dashed #545454";
|
|
|
|
replacement.style.padding = "2em";
|
|
|
|
replacement.style.margin = "auto";
|
|
|
|
App.all_replacements[i] = replacement;
|
|
|
|
App.backup_videos[i] = video;
|
|
|
|
video.parentNode.replaceChild(replacement, video);
|
|
|
|
}
|
|
|
|
|
|
|
|
function restore_video(obj, index) {
|
|
|
|
obj = App.all_replacements[index];
|
|
|
|
video = App.backup_videos[index];
|
|
|
|
console.log(video);
|
|
|
|
obj.parentNode.replaceChild(video, obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** force repainting the video, thanks to:
|
|
|
|
* http://martinwolf.org/2014/06/10/force-repaint-of-an-element-with-javascript/
|
|
|
|
*/
|
|
|
|
var siteHeader = document.getElementById('header');
|
|
|
|
siteHeader.style.display='none';
|
|
|
|
siteHeader.offsetHeight; // no need to store this anywhere, the reference is enough
|
|
|
|
siteHeader.style.display='block';
|
|
|
|
|
|
|
|
EOF
|
|
|
|
}
|
|
|
|
|
|
|
|
printjs() {
|
|
|
|
js | sed 's,//.*$,,' | tr '\n' ' '
|
|
|
|
}
|
|
|
|
echo "jseval -q $(printjs)" >> "$QUTE_FIFO"
|
|
|
|
|
|
|
|
tmpdir=$(mktemp -d)
|
|
|
|
file_to_cast=${tmpdir}/qutecast
|
2017-11-05 02:35:04 +01:00
|
|
|
program_=$(command -v castnow)
|
|
|
|
|
|
|
|
if [[ "${program_}" == "" ]]; then
|
|
|
|
msg error "castnow can't be found..."
|
|
|
|
exit 1
|
|
|
|
fi
|
2016-10-11 10:37:35 +02:00
|
|
|
|
|
|
|
# kill any running instance of castnow
|
2017-11-05 02:35:04 +01:00
|
|
|
pkill -f "${program_}"
|
2016-10-11 10:37:35 +02:00
|
|
|
|
|
|
|
# start youtube download in stream mode (-o -) into temporary file
|
|
|
|
youtube-dl -qo - "$1" > ${file_to_cast} &
|
|
|
|
ytdl_pid=$!
|
|
|
|
|
|
|
|
msg info "Casting $1" >> "$QUTE_FIFO"
|
|
|
|
# start castnow in stream mode to cast on ChromeCast
|
2017-11-05 02:35:04 +01:00
|
|
|
tail -F "${file_to_cast}" | ${program_} -
|
2016-10-11 10:37:35 +02:00
|
|
|
|
|
|
|
# cleanup remaining background process and file on disk
|
|
|
|
kill ${ytdl_pid}
|
|
|
|
rm -rf ${tmpdir}
|