Skip to content

Instantly share code, notes, and snippets.

@HelloWorld017
Last active September 17, 2022 05:58
Show Gist options
  • Save HelloWorld017/805c0551ade99654925062a6b46da7c9 to your computer and use it in GitHub Desktop.
Save HelloWorld017/805c0551ade99654925062a6b46da7c9 to your computer and use it in GitHub Desktop.
Capture twitch streaming or download clip.
// ==UserScript==
// @name Twitch Capture Now
// @namespace https://gist.github.com/HelloWorld017/805c0551ade99654925062a6b46da7c9
// @version 1.0.2
// @description Capture twitch streaming
// @author Khinenw
// @match *://*.twitch.tv/*
// @grant none
// ==/UserScript==
const capture = () => {
const $canvas = document.createElement('canvas');
const $ctx = $canvas.getContext('2d');
const $videoElem = document.querySelector('.video-player__container video');
//document.querySelector('.highwind-video-player__container video');
$canvas.width = $videoElem.videoWidth;
$canvas.height = $videoElem.videoHeight;
$canvas.style.zIndex = 999;
$canvas.style.position = 'fixed';
$canvas.style.top = 0;
$canvas.style.left = 0;
$ctx.drawImage($videoElem, 0, 0);
$canvas.toBlob(blob => {
const objUrl = URL.createObjectURL(blob);
downloadSrc(objUrl);
setTimeout(() => URL.revokeObjectURL(objUrl), 3000);
});
};
const download = () => {
const $videoElem = document.querySelector('.video-player__container video');
//document.querySelector('.highwind-video-player__container video');
downloadSrc($videoElem.src);
};
const downloadSrc = src => {
const a = document.createElement('a');
a.href = src;
a.download = '';
a.click();
};
const addButton = (panels, htmlContent, cb) => {
const button = document.createElement('button');
button.classList.add('player-button');
button.style.margin = "0 5px";
button.style.display = "inline-flex";
button.type = 'button';
button.innerHTML = htmlContent;
button.addEventListener('click', cb);
button.querySelector('svg').style.width = '20px';
panels.appendChild(button);
};
const appendButton = panels => {
addButton(panels, `<svg viewBox="0 0 20 20" height="20" width="20" fill="#fff">
<circle cx="13" cy="10" r="3.5"></circle>
<path d="M0 4v12h20V4H0z M7 7H1V5h6V7z M13 15c-2.8 0-5-2.2-5-5s2.2-5 5-5s5 2.2 5 5S15.8 15 13 15z"></path>
</svg>`, capture);
panels.$TwitchCaptureAppend = true;
};
const appendClipButton = panels => {
addButton(panels, `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="12" height="14" viewBox="0 0 12 14" fill="#fff">
<path d="M10 10.5q0-0.203-0.148-0.352t-0.352-0.148-0.352 0.148-0.148 0.352 0.148 0.352 0.352 0.148 0.352-0.148 0.148-0.352zM12 10.5q0-0.203-0.148-0.352t-0.352-0.148-0.352 0.148-0.148 0.352 0.148 0.352 0.352 0.148 0.352-0.148 0.148-0.352zM13 8.75v2.5q0 0.312-0.219 0.531t-0.531 0.219h-11.5q-0.312 0-0.531-0.219t-0.219-0.531v-2.5q0-0.312 0.219-0.531t0.531-0.219h3.633l1.055 1.062q0.453 0.438 1.062 0.438t1.062-0.438l1.062-1.062h3.625q0.312 0 0.531 0.219t0.219 0.531zM10.461 4.305q0.133 0.32-0.109 0.547l-3.5 3.5q-0.141 0.148-0.352 0.148t-0.352-0.148l-3.5-3.5q-0.242-0.227-0.109-0.547 0.133-0.305 0.461-0.305h2v-3.5q0-0.203 0.148-0.352t0.352-0.148h2q0.203 0 0.352 0.148t0.148 0.352v3.5h2q0.328 0 0.461 0.305z"></path>
</svg>`, download);
panels.$TwitchClipAppend = true;
};
setInterval(() => {
const panels = document.querySelector('.player-controls__right-control-group');
const isClip = !!document.querySelector('.video-player__container video[src^="https://clips"]');
if(panels && !panels.$TwitchCaptureAppend) appendButton(panels);
if(isClip && !panels.$TwitchClipAppend) appendClipButton(panels);
}, 3000);
@tballas
Copy link

tballas commented Sep 17, 2022

I updated this since I use it fairly often. For this to work you also need to use another userscript, here, I made that adds back video.crossOrigin = "anonymous", which twitch seems to have removed, for twitch streams so the screenshots work. Can't screenshot clips, unfortunately, but clip downloading does work.

// ==UserScript==
// @name         Twitch Capture Now
// @namespace    https://gist.github.com/HelloWorld017/805c0551ade99654925062a6b46da7c9
// @version      1.0.5
// @description  Capture twitch streaming
// @author       Khinenw
// @author       @tim1986
// @match        *://*.twitch.tv/*
// @grant        GM_download
// ==/UserScript==

const debug = false;

const capture = () => {
    const $canvas = document.createElement('canvas');
    const $ctx = $canvas.getContext('2d');
    const $videoElem = document.querySelector('.video-player__container video');

    $canvas.width = $videoElem.videoWidth;
    $canvas.height = $videoElem.videoHeight;
    $canvas.style.zIndex = 999;
    $canvas.style.position = 'fixed';
    $canvas.style.top = 0;
    $canvas.style.left = 0;

    $ctx.drawImage($videoElem, 0, 0);

    $canvas.toBlob(blob => {
		const objUrl = URL.createObjectURL(blob);
        const fileType = blob.type.split('/')[blob.type.split('/').length-1];
        const blobName = objUrl.split('/')[objUrl.split('/').length-1];
        if (debug) {
            console.log('[Capture Now, Twitch]', blob, objUrl, fileType, blobName);
        }
        try {
            GM_download(objUrl, blobName + '.' + fileType);
        } catch(e) {
            downloadSrc(objUrl);
        }
		setTimeout(() => URL.revokeObjectURL(objUrl), 3000);
	});
};

const download = () => {
    const $videoElem = document.querySelector('.video-player__container video');
    downloadSrc($videoElem.src);
};

const downloadSrc = src => {
	const a = document.createElement('a');
    a.href = src;
    a.download = '';
    a.click();
};

const addButton = (panels, htmlContent, cb) => {
    // cb is Callback
    const button = document.createElement('button');
    button.classList.add('player-button');
	button.style.margin = "0 5px";
	button.style.display = "inline-flex";
    button.type = 'button';
    button.innerHTML = htmlContent;
    button.addEventListener('click', cb);
    button.querySelector('svg').style.width = '20px';
    button.id = 'CaptureNow';
    panels.appendChild(button);
};

const appendButton = panels => {
    addButton(panels, `<svg viewBox="0 0 20 20" height="20" width="20" fill="#fff">
<circle cx="13" cy="10" r="3.5"></circle>
<path d="M0 4v12h20V4H0z M7 7H1V5h6V7z M13 15c-2.8 0-5-2.2-5-5s2.2-5 5-5s5 2.2 5 5S15.8 15 13 15z"></path>
</svg>`, capture);

    panels.$TwitchCaptureAppend = true;
};

const appendClipButton = panels => {
    addButton(panels, `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="12" height="14" viewBox="0 0 12 14" fill="#fff">
				<path d="M10 10.5q0-0.203-0.148-0.352t-0.352-0.148-0.352 0.148-0.148 0.352 0.148 0.352 0.352 0.148 0.352-0.148 0.148-0.352zM12 10.5q0-0.203-0.148-0.352t-0.352-0.148-0.352 0.148-0.148 0.352 0.148 0.352 0.352 0.148 0.352-0.148 0.148-0.352zM13 8.75v2.5q0 0.312-0.219 0.531t-0.531 0.219h-11.5q-0.312 0-0.531-0.219t-0.219-0.531v-2.5q0-0.312 0.219-0.531t0.531-0.219h3.633l1.055 1.062q0.453 0.438 1.062 0.438t1.062-0.438l1.062-1.062h3.625q0.312 0 0.531 0.219t0.219 0.531zM10.461 4.305q0.133 0.32-0.109 0.547l-3.5 3.5q-0.141 0.148-0.352 0.148t-0.352-0.148l-3.5-3.5q-0.242-0.227-0.109-0.547 0.133-0.305 0.461-0.305h2v-3.5q0-0.203 0.148-0.352t0.352-0.148h2q0.203 0 0.352 0.148t0.148 0.352v3.5h2q0.328 0 0.461 0.305z"></path>
			</svg>`, download);

    panels.$TwitchClipAppend = true;
};

setInterval(() => {
    let panels;
    panels = document.querySelectorAll('.player-controls__right-control-group')[1];
    if (panels == null) {
        panels = document.querySelector('.player-controls__right-control-group');
    }
    const isClip = !!document.querySelector('.video-player__container video[src^="https://production.assets.clips"]');
    if(panels && !panels.$TwitchCaptureAppend && !isClip) appendButton(panels);
    if(isClip && !panels.$TwitchClipAppend) appendClipButton(panels);
}, 3000);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment