YouToolBox

A userscript with many utility functions!

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name        YouToolBox
// @namespace   Violentmonkey Scripts
// @match       https://www.youtube.com/watch*
// @version     1.0
// @author      CodeOS99
// @description A userscript with many utility functions!
// @grant       none
// @license     MIT
// ==/UserScript==

(function () {
    'use strict';
    console.log("YTB Active!");

    // COLOURS
    const lightGray = '#A9A9A9';
    const darkRed = '#8B0000';
    const darkerRed = '#900C3F';

    let menuHeld = false; // if the menu is held
    let menuOx = 0, menuOy = 0; // menu offset x and y

    // Wait until the page is fully ready to add stuff
    window.addEventListener('load', () => {
        makeMenu(); // make the menu button initially
    });

    function makeMenu() {
        const style = document.createElement('style');
            style.textContent = `
                #main-menu button {
                    cursor: pointer !important;
                }`;
        document.head.appendChild(style);

        let div = document.createElement('div');
        div.id = "main-menu";
        Object.assign(div.style, {
            position: 'fixed',
            top: '80px',
            left: '20px',
            zIndex: 9999,
            display: 'flex',
            alignItems: 'center',
            gap: '1px',
            userSelect: 'none',
            flexDirection: 'column'
        });

        let mover = document.createElement('div');
        Object.assign(mover.style, {
            width: '50px',
            height: '50px',
            backgroundColor: '#444',  // make it visible
            marginLeft: '10px',
            borderRadius: '8px',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            color: '#fff',
            fontWeight: 'bold',
            fontFamily: 'sans-serif',
            cursor: 'move',
        });
        mover.textContent = 'Move';

        let main_menu_btn = document.createElement('button');
        main_menu_btn.id = 'main-menu-btn'

        main_menu_btn.textContent = 'Show menu?';

        Object.assign(main_menu_btn.style, {
            top: '80px',
            left: '20px',
            zIndex: 9999,
            padding: '10px',
            fontSize: '14px',
            background: '#f33',
            color: '#fff',
            border: 'none',
            borderRadius: '4px',
        });

        let topMenuDiv = document.createElement('div');
        Object.assign(topMenuDiv.style, {
            width: '200px',
            height: '50px',
            backgroundColor: '#444',  // make it visible
            marginLeft: '10px',
            borderRadius: '8px',
            display: 'none',
            alignItems: 'center',
            justifyContent: 'center',
            color: '#fff',
            fontWeight: 'bold',
            fontFamily: 'sans-serif',
        });

        let closeMenuBtn = document.createElement('button');
        closeMenuBtn.innerText = "X"
        Object.assign(closeMenuBtn.style, {
            top: '20px',
            left: '20px',
            width: '20px',
            zIndex: 9999,
            marginLeft: '10px',
            fontSize: '14px',
            background: 'red',
            color: '#fff',
            border: 'none',
            borderRadius: '4px',
            fontSize: '10px',
        });

        let noteModeButton = document.createElement('button');
        noteModeButton.innerText = "NOTES"
        Object.assign(noteModeButton.style, {
            top: '80px',
            left: '20px',
            width: '50px',
            zIndex: 9999,
            marginLeft: '10px',
            fontSize: '14px',
            background: lightGray,
            color: '#fff',
            border: 'none',
            borderRadius: '4px',
            fontSize: '10px',
        });

        let calculatorModeBtn = document.createElement('button');
        calculatorModeBtn.innerText = "CALC."
        Object.assign(calculatorModeBtn.style, {
            top: '80px',
            left: '20px',
            width: '50px',
            zIndex: 9999,
            marginLeft: '10px',
            marginRight: '10px',
            fontSize: '14px',
            background: lightGray,
            color: '#fff',
            border: 'none',
            borderRadius: '4px',
            fontSize: '10px'
        });

        let quickSettingsModeBtn = document.createElement('button');
        quickSettingsModeBtn.innerText = "SETTINGS"
        Object.assign(quickSettingsModeBtn.style, {
            top: '80px',
            left: '20px',
            width: '50px',
            zIndex: 9999,
            marginLeft: '10px',
            fontSize: '14px',
            background: lightGray,
            color: '#fff',
            border: 'none',
            borderRadius: '4px',
            fontSize: '10px'
        });

        topMenuDiv.appendChild(closeMenuBtn);
        topMenuDiv.appendChild(quickSettingsModeBtn);
        topMenuDiv.appendChild(noteModeButton);
        topMenuDiv.appendChild(calculatorModeBtn);

        let menuBtns = [noteModeButton, calculatorModeBtn, quickSettingsModeBtn];
        menuBtns.forEach(btn => {
            btn.onmouseenter = () => {
                btn.style.backgroundColor = 'gray';
            }

            btn.onmouseleave = () => {
                btn.style.backgroundColor = lightGray;
            }

            btn.onmousedown = () => {
                btn.style.backgroundColor = 'black'
            }
        });

        noteModeButton.onmouseup = () => {
            noteInput.style.display = 'block';
            calculatorDiv.style.display = 'none';
            quickSettingsDiv.style.display = 'none';
        }

        closeMenuBtn.onmouseover = () => {
            closeMenuBtn.style.backgroundColor = darkRed;
        };            
        closeMenuBtn.onmouseleave = () => {
            closeMenuBtn.style.backgroundColor = 'red';
        };
        closeMenuBtn.onmouseup = () => {
            closeMenuBtn.style.backgroundColor = darkerRed;
            bottomMenuDiv.style.display = 'none';
            topMenuDiv.style.display = 'none';

            main_menu_btn.style.display = 'block';

            btn.style.backgroundColor = lightGray;
        }

        let bottomMenuDiv = document.createElement('div');        
        Object.assign(bottomMenuDiv.style, {
            width: '200px',
            height: '50px',
            backgroundColor: '#444',
            marginLeft: '10px',
            borderRadius: '8px',
            display: 'none',
            alignItems: 'center',
            justifyContent: 'center',
            color: '#fff',
            fontWeight: 'bold',
            fontFamily: 'sans-serif',
            resize: 'both',
            overflow: 'auto'
        });

        let noteInput = document.createElement('textarea');
        noteInput.value = localStorage.getItem('ytb_notes') || '';
        noteInput.addEventListener('input', () => {
            localStorage.setItem('ytb_notes', noteInput.value);
        });
        noteInput.style.display = 'none';

        const buttonLabels = [
            '7', '8', '9', '/',
            '4', '5', '6', '*',
            '1', '2', '3', '-',
            '0', '.', '=', '+',
            'CLR', 'DEL', '(', ')',
        ];
        let calculatorBtns = [];

        let calculatorExpr = '';

        let calculatorDiv = document.createElement('div');
        Object.assign(calculatorDiv.style, {
            display: 'none',
            alignItems: 'center',
            justifyContent: 'center',
        });

        let calcExprH1 = document.createElement('h1');
        calcExprH1.innerText = "0";
        Object.assign(calcExprH1.style, {
            backgroundColor: 'gray',
            borderRadius: '5px',
        })

        calculatorDiv.appendChild(calcExprH1);

        let calculatorBtnDiv = document.createElement('div');
        Object.assign(calculatorBtnDiv.style, {
            display: 'grid',
            gridTemplateColumns: 'repeat(4, 50px)',
            gap: '6px',
            padding: '10px',
            justifyContent: 'center',
            alignItems: 'center'
        });

        calculatorDiv.appendChild(calculatorBtnDiv);

        bottomMenuDiv.appendChild(calculatorDiv);

        for (let i = 0; i < buttonLabels.length; i++) {
            let btn  = document.createElement('button');
            btn.innerText = buttonLabels[i];

            Object.assign(btn.style, {
                backgroundColor: lightGray,
                color: 'white',
                border: 'none',
                borderRadius: '5px',
                padding: '15px',
                fontSize: '16px',
                cursor: 'pointer',
                width: '50px',
                height: '50px',
            });

            btn.onmouseenter = () => {
                btn.style.backgroundColor = 'gray';
            }

            btn.onmouseleave = () => {
                btn.style.backgroundColor = lightGray;
            }

            btn.onmousedown = () => {
                btn.style.backgroundColor = 'black'
            }

            btn.onmouseup = () => {
                btn.style.backgroundColor = lightGray;
                updateCalcExpr(buttonLabels[i]);
            }

            calculatorBtnDiv.appendChild(btn);

            calculatorBtns.push(btn);
        }

        function updateCalcExpr(val) {
            if(val !== 'CLR' && val !== 'DEL' && val !== '=') {
                if(calculatorExpr !== '0') calculatorExpr += val;
                else calculatorExpr = val;
            }
            else if(val === 'CLR') calculatorExpr = '0';
            else if(val === 'DEL') {
                calculatorExpr = calculatorExpr.substring(0, calculatorExpr.length-1)
                if(calculatorExpr === '') {
                    calculatorExpr = '0';
                }
            }
            else if(val === '=') {
                if(!'+-/*.'.includes(calculatorExpr[calculatorExpr.length - 1])) {
                    try {
                        calculatorExpr = eval(calculatorExpr).toString();
                    } catch(e) {
                        alert("You made an error in the expression!");
                        calculatorExpr = '0';
                    }
                }
            }

            calcExprH1.innerText = calculatorExpr;
        }

        let showCalculator = () => {
            calculatorDiv.style.display = 'flex'
            quickSettingsDiv.style.display = 'none';
            noteInput.style.display = 'none';
        }

        bottomMenuDiv.appendChild(noteInput);

        calculatorModeBtn.onmousedown = () => {
            calculatorModeBtn.style.backgroundColor = 'black';
            showCalculator();
        }

        let quickSettingsDiv = document.createElement('div');
        
        const layout = [0.5, 0.75, 1, 1.25, 1.5,
                        144, 240, 360, 480, 720
        ];

        Object.assign(quickSettingsDiv.style, {
            display: 'grid',
            gridTemplateColumns: 'repeat(5, 1fr)', // Equal size columns
            gap: '6px',
            padding: '10px',
            justifyContent: 'center',
            alignItems: 'center',
        });


        for(let btnSpec of layout) {
            let btn = document.createElement('button');
            if(btnSpec <= 1.5) {
                if(btnSpec < 1)
                    btn.innerText = `x${btnSpec} 🐌`
                else if(btnSpec == 1)
                    btn.innerText = `x${btnSpec} 🐍`
                else
                    btn.innerText = `x${btnSpec} 🐇`

                btn.onmouseup = () => {
                    let player = document.querySelector('video');
                    player.playbackRate = btnSpec;
                    btn.style.backgroundColor = lightGray;
                }
            } else {
                btn.innerText = `${btnSpec}p`
                btn.onclick = () => {
                    // Video quality
                    let qualityMap = {
                        144: 'tiny',
                        240: 'small',
                        360: 'medium',
                        480: 'large',
                        720: 'hd720',
                    };
                    setQuality(qualityMap[btnSpec]);
                }
            }
            Object.assign(btn.style, {
                padding: '10px',
                fontSize: '14px',
                backgroundColor: lightGray,
                color: '#fff',
                border: 'none',
                borderRadius: '4px',
                cursor: 'pointer',
                width: '80px', 
            });

            btn.onmouseenter = () => {
                btn.style.backgroundColor = 'gray';
            }

            btn.onmouseleave = () => {
                btn.style.backgroundColor = lightGray;
            }

            btn.onmousedown = () => {
                btn.style.backgroundColor = 'black'
            }

            quickSettingsDiv.appendChild(btn);
        }

        function setQuality(qualityLabel) {
            const player = document.getElementById('movie_player');
            if (player && player.setPlaybackQualityRange) {
                player.setPlaybackQualityRange(qualityLabel);
                player.setPlaybackQuality(qualityLabel);
            }
        }

        function showQuickSettings() {
            quickSettingsDiv.style.display = 'grid';
            calculatorDiv.style.display = 'none';
            noteInput.style.display = 'none';
        }

        quickSettingsModeBtn.onmouseup = () => {
            showQuickSettings();
        }

        bottomMenuDiv.appendChild(quickSettingsDiv);

        mover.onmousedown = (e) => {
            if(!menuHeld) {
                menuHeld = true;
                menuOx = e.clientX - div.offsetLeft;
                menuOy = e.clientY - div.offsetTop;
                document.body.style.userSelect = 'none';
            }
        }

        document.onmouseup = () => { // add the event listener to the body because there is a possibility for the mouse to move away from the button
            menuHeld = false;
        }

        document.onmousemove = (e) => {
            if(menuHeld) {
                div.style.left = (e.clientX - menuOx) + 'px';
                div.style.top = (e.clientY - menuOy) + 'px';
            }
        }

        main_menu_btn.onclick = () => {
            main_menu_btn.style.display = 'none';

            topMenuDiv.style.display = 'flex';
            bottomMenuDiv.style.display = 'flex';
        };

        div.appendChild(mover);
        div.appendChild(main_menu_btn);

        div.appendChild(topMenuDiv);
        div.appendChild(bottomMenuDiv);

        document.body.appendChild(div);
    }
})();