YouTube: Take screenshots using hotkey

動画のスクリーンショットを撮るキーボードショートカットを追加する

当前为 2021-12-25 提交的版本,查看 最新版本

// ==UserScript==
// @name        YouTube: Take screenshots using hotkey
// @description 動画のスクリーンショットを撮るキーボードショートカットを追加する
// @namespace   https://gitlab.com/sigsign
// @version     0.2.0
// @author      Sigsign
// @license     MIT or Apache-2.0
// @match       https://www.youtube.com/*
// @grant       none
// ==/UserScript==
(function () {
'use strict';

function getPlayer() {
    return document.querySelector('#movie_player');
}

const notifications = new Set();
function capture(video) {
    return new Promise((resolv, reject) => {
        const canvas = document.createElement('canvas');
        canvas.width = video.videoWidth;
        canvas.height = video.videoHeight;
        const ctx = canvas.getContext('2d');
        if (!ctx) {
            return reject('[Err] canvas.getContext() is failed');
        }
        ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
        canvas.toBlob((blob) => {
            if (!blob) {
                return reject('[Err] blob is null');
            }
            resolv(blob);
        }, 'image/png');
    });
}
// [Firefox] dom.events.asyncClipboard.clipboardItem => True
async function copyToClipboard(blob) {
    // eslint-disable-next-line no-undef
    const clipboardItem = new ClipboardItem({
        [blob.type]: blob
    });
    await navigator.clipboard.write([clipboardItem]);
}
function getIcon() {
    const icon = document.querySelector('ytd-video-owner-renderer img');
    return icon ? icon.src : '';
}
function getTitle() {
    const player = getPlayer();
    if (!player || typeof player.getVideoData !== 'function') {
        return '';
    }
    const data = player.getVideoData() || {};
    return data.title || '';
}
async function notify(title, icon, image) {
    if (Notification.permission === 'denied') {
        return;
    }
    if (Notification.permission !== 'granted') {
        if (await Notification.requestPermission() !== 'granted') {
            return;
        }
    }
    for (const n of notifications) {
        n.close();
    }
    notifications.clear();
    const url = URL.createObjectURL(image);
    const options = {
        body: title,
        icon: icon,
        image: url,
        silent: true,
    };
    const n = new Notification('Take a screenshot', options);
    setTimeout(() => {
        n.close();
    }, 3 * 1000);
    n.onclose = () => {
        URL.revokeObjectURL(url);
        notifications.delete(n);
    };
    notifications.add(n);
}
function readyStream(list) {
    if (list.contains('playing-mode') || list.contains('paused-mode')) {
        return true;
    }
    return false;
}
async function takeScreenshot() {
    const player = getPlayer();
    if (!player || !readyStream(player.classList)) {
        throw new Error('[Err] YouTube Player is not ready');
    }
    const video = player.querySelector('video');
    if (!video) {
        throw new Error('[Err] HTMLVideoElement is not exists');
    }
    const blob = await capture(video);
    copyToClipboard(blob);
    const text = getTitle();
    const icon = getIcon();
    notify(text, icon, blob);
}
function init() {
    const path = location.pathname;
    // チャット欄にフォーカスがあってもスクショを撮れるようにする
    if (path.includes('/live_chat') || path.includes('/live_chat_replay')) {
        window.addEventListener('keydown', function (ev) {
            if (ev.target === window.document.body && ev.key === 'q') {
                const message = {
                    type: 'userscript-take-screenshots'
                };
                window.parent.postMessage(message);
            }
        }, false);
        // チャット欄でやることはもうない
        return;
    }
    // 最上位のフレームだけが postMessage() を受け取る
    if (window.top === window.self) {
        window.addEventListener('message', (ev) => {
            const url = new URL(ev.origin);
            if (url.hostname !== 'www.youtube.com' || !ev.data) {
                return;
            }
            const message = ev.data;
            if (message.type === 'userscript-take-screenshots') {
                takeScreenshot().catch((err) => {
                    console.error(err);
                });
            }
        });
    }
    window.addEventListener('keydown', (ev) => {
        if (ev.key === 'q') {
            takeScreenshot().catch((err) => {
                console.error(err);
            });
        }
    }, false);
}
init();

})();