summaryrefslogtreecommitdiff
path: root/misc/userscripts/cast
blob: 609a7d0db184c547e9678c0bfaca43d7704fc0dd (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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
#!/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
#   - youtube-dl (https://youtube-dl.org/) or a drop-in replacement such as
#     yt-dlp (https://github.com/yt-dlp/yt-dlp).
#
# Configuration:
#   This script looks at the optional QUTE_CAST_YTDL_PROGRAM environment
#   variable (if it exists) to decide which program to use for downloading
#   videos. If specified, this should be youtube-dl or a drop-in replacement
#   for it.
#
# 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
cast_program=$(command -v castnow)

# pick a ytdl program
for p in "$QUTE_CAST_YTDL_PROGRAM" yt-dlp youtube-dl; do
    ytdl_program=$(command -v -- "$p")
    [ "$ytdl_program" == "" ] || break
done

if [[ "${cast_program}" == "" ]]; then
    msg error "castnow can't be found"
    exit 1
fi
if [[ "${ytdl_program}" == "" ]]; then
    msg error "youtube-dl or a drop-in replacement can't be found in PATH, and no installed program "\
"specified in QUTE_CAST_YTDL_PROGRAM (currently \\\"$QUTE_CAST_YTDL_PROGRAM\\\")"
    exit 1
fi

# kill any running instance of castnow
pkill -f -- "${cast_program}"

# start youtube download in stream mode (-o -) into temporary file
"${ytdl_program}" -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}" | ${cast_program} -

# cleanup remaining background process and file on disk
kill ${ytdl_pid}
rm -rf "${tmpdir}"