Greasy Fork is available in English.

youtubeプレビュー再生機能追加

Youtubeをプレビュー再生

// ==UserScript==
// @name         youtubeプレビュー再生機能追加
// @name:en      Added youtube preview playback function
// @license AMA
// @namespace    http://tampermonkey.net/
// @version      2.9
// @description     Youtubeをプレビュー再生
// @description:en  Preview playback of Youtube
// @author       Your Name
// @match        https://www.youtube.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    let previewVideo = null;
    let currentVideoId = null;
    let player;
    const sizes = [
        { width: '320px', height: '180px' },
        { width: '640px', height: '360px' },
        { width: '1280px', height: '720px' }
    ];
    let currentSizeIndex = 1;

    document.addEventListener('mouseover', function(e) {
        let thumbnail = e.target.closest('a#thumbnail');
        if (thumbnail) {
            let url = new URL(thumbnail.href);
            if (url.pathname.startsWith('/shorts/')) {
                currentVideoId = url.pathname.split('/shorts/')[1];
            } else {
                currentVideoId = url.searchParams.get('v');
            }

            // 左上にプレビュー再生ボタンを追加
            addPreviewButton(thumbnail);
        }
    });

    function addPreviewButton(thumbnail) {
        let previewButton = thumbnail.querySelector('.preview-button');
        if (!previewButton) {
            previewButton = document.createElement('button');
            previewButton.textContent = 'prev';
            previewButton.className = 'preview-button';
            previewButton.style.position = 'absolute';
            previewButton.style.top = '5px';
            previewButton.style.left = '5px';
            previewButton.style.zIndex = '1001';
            previewButton.style.backgroundColor = 'rgba(255, 255, 255, 0.5)'; // 透明度50%
            previewButton.style.border = '1px solid #ccc';
            previewButton.style.padding = '5px';
            previewButton.style.cursor = 'pointer';
            previewButton.style.width = thumbnail.offsetWidth + 'px'; // サムネイルと同じ幅
            previewButton.style.height = '50px'; // 高さを50pxに設定
            previewButton.addEventListener('click', function(event) {
                event.stopPropagation(); // クリックイベントの伝播を止める
                event.preventDefault(); // デフォルトの動作を防ぐ
                if (previewVideo) {
                    stopPreview();
                }
                startPreview(currentVideoId);
            });
            thumbnail.appendChild(previewButton);

            thumbnail.addEventListener('mouseleave', function() {
                previewButton.style.display = 'none';
            });

            thumbnail.addEventListener('mouseenter', function() {
                previewButton.style.display = 'block';
            });
        }
    }

    document.addEventListener('keydown', function(event) {
        if (event.key === 'V' && event.shiftKey && currentVideoId) {
            if (previewVideo) {
                stopPreview();
            }
            startPreview(currentVideoId);
        }
        if (event.key === 'Escape' && previewVideo) {
            stopPreview();
        }
    });

    function startPreview(videoId) {
        let previewUrl = videoId.includes('shorts') ?
            `https://www.youtube.com/embed/${videoId}` :
            `https://www.youtube.com/embed/${videoId}?autoplay=1&enablejsapi=1`;

        previewVideo = document.createElement('div');
        previewVideo.style.position = 'fixed';
        previewVideo.style.bottom = '10px';
        previewVideo.style.right = '10px';
        previewVideo.style.zIndex = '1000';
        previewVideo.style.backgroundColor = 'black';
        previewVideo.style.width = sizes[currentSizeIndex].width;
        previewVideo.style.height = sizes[currentSizeIndex].height;

        let iframe = document.createElement('iframe');
        iframe.src = previewUrl;
        iframe.style.width = '100%';
        iframe.style.height = '100%';
        iframe.style.border = 'none';
        previewVideo.appendChild(iframe);

        let resizeButton = document.createElement('button');
        resizeButton.textContent = '⬜';
        resizeButton.style.position = 'absolute';
        resizeButton.style.top = '10px';
        resizeButton.style.right = '10px';
        resizeButton.style.zIndex = '1001';
        resizeButton.style.backgroundColor = 'rgba(255, 255, 255, 0.5)'; // 透明度50%
        resizeButton.style.border = '1px solid #ccc';
        resizeButton.style.padding = '5px';
        resizeButton.style.cursor = 'pointer';
        resizeButton.addEventListener('click', function() {
            currentSizeIndex = (currentSizeIndex + 1) % sizes.length;
            previewVideo.style.width = sizes[currentSizeIndex].width;
            previewVideo.style.height = sizes[currentSizeIndex].height;
        });
        previewVideo.appendChild(resizeButton);

        let closeButton = document.createElement('button');
        closeButton.textContent = '✕';
        closeButton.style.position = 'absolute';
        closeButton.style.top = '10px';
        closeButton.style.left = '10px';
        closeButton.style.zIndex = '1001';
        closeButton.style.backgroundColor = 'rgba(255, 255, 255, 0.5)'; // 透明度50%
        closeButton.style.border = '1px solid #ccc';
        closeButton.style.padding = '5px';
        closeButton.style.cursor = 'pointer';
        closeButton.addEventListener('click', function() {
            stopPreview();
        });
        previewVideo.appendChild(closeButton);

        document.body.appendChild(previewVideo);

        // プレーヤーを初期化
        player = new YT.Player(iframe, {
            events: {
                'onReady': onPlayerReady
            }
        });
    }

    function stopPreview() {
        if (player) {
            player.stopVideo();
        }
        if (previewVideo) {
            previewVideo.remove();
            previewVideo = null;
        }
    }

    // プレーヤーが準備完了したときの処理
    function onPlayerReady(event) {
        event.target.setVolume(50); // 音量を50に設定
        event.target.playVideo();
    }

})();