#!/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 program_=$(command -v castnow) if [[ "${program_}" == "" ]]; then msg error "castnow can't be found..." exit 1 fi # kill any running instance of castnow pkill -f "${program_}" # 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 tail -F "${file_to_cast}" | ${program_} - # cleanup remaining background process and file on disk kill ${ytdl_pid} rm -rf "${tmpdir}"