summaryrefslogtreecommitdiff
path: root/misc/userscripts/cast
blob: df74fe97e3cc7862a7f3c908f346960de306b744 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#!/usr/bin/env bash
#
# Behavior
#   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://web.archive.org/web/20151029064649/https://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}"