YouTube Settings Rearranger

Rearranges "Playback Speed" and "Quality" settings in YouTube video settings for improved accessibility.

// ==UserScript==
// @name         YouTube Settings Rearranger
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Rearranges "Playback Speed" and "Quality" settings in YouTube video settings for improved accessibility.
// @author       malordin
// @match        https://www.youtube.com/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // Debug mode flag
    const DEBUG = true;

    /**
     * Logs messages to the console with a custom tag.
     * @param {string} message - The message to log.
     * @param {string} type - The type of log ('log', 'warn', 'error').
     */
    function log(message, type = 'log') {
        if (!DEBUG) return;
        const LOG_TAG = '[YouTubeSettingsRearranger]';
        switch(type) {
            case 'log':
                console.log(`${LOG_TAG} ${message}`);
                break;
            case 'warn':
                console.warn(`${LOG_TAG} ${message}`);
                break;
            case 'error':
                console.error(`${LOG_TAG} ${message}`);
                break;
            default:
                console.log(`${LOG_TAG} ${message}`);
        }
    }

    // Flag to track if rearrangement has been done for the current menu open
    let rearranged = false;

    // SVG path prefixes for identifying menu items
    const PLAYBACK_SPEED_SVG_PREFIX = "M10,8v8l6-4L10,8L10,8z";
    const QUALITY_SVG_PREFIX = "M15,17h6v1h-6V17z";

    /**
     * Identifies the "Playback Speed" and "Quality" menu items based on their SVG icons.
     * @returns {Object} An object containing the playbackSpeedItem and qualityItem elements.
     */
    function identifyMenuItems() {
        const menuItems = document.querySelectorAll('.ytp-panel-menu .ytp-menuitem');
        log(`Found ${menuItems.length} menu items`);

        let playbackSpeedItem = null;
        let qualityItem = null;

        menuItems.forEach((item, index) => {
            const svgPath = item.querySelector('.ytp-menuitem-icon svg path');
            if (svgPath) {
                const dAttribute = svgPath.getAttribute('d');
                if (dAttribute.startsWith(PLAYBACK_SPEED_SVG_PREFIX)) {
                    playbackSpeedItem = item;
                    log('Found "Playback Speed"');
                }
                if (dAttribute.startsWith(QUALITY_SVG_PREFIX)) {
                    qualityItem = item;
                    log('Found "Quality"');
                }
            } else {
                log(`Item ${index + 1} does not contain an SVG path`, 'warn');
            }
        });

        return { playbackSpeedItem, qualityItem, menuItems };
    }

    /**
     * Rearranges the "Playback Speed" menu item to be immediately before the "Quality" menu item.
     */
    function rearrangeSettingsMenu() {
        log('Initializing menu rearrangement');

        const { playbackSpeedItem, qualityItem, menuItems } = identifyMenuItems();

        // Check if both items are found
        if (playbackSpeedItem && qualityItem) {
            // Check if "Playback Speed" is already immediately before "Quality"
            const nextSibling = playbackSpeedItem.nextElementSibling;
            if (nextSibling === qualityItem) {
                log('Rearrangement not needed, items are already in the correct order');
                return;
            }

            try {
                // Move "Playback Speed" before "Quality"
                qualityItem.parentNode.insertBefore(playbackSpeedItem, qualityItem);
                log('Rearrangement successful');
                rearranged = true; // Set the flag to prevent repeated rearrangement
            } catch (error) {
                log(`Error during rearrangement: ${error}`, 'error');
            }
        } else {
            if (!playbackSpeedItem) {
                log('"Playback Speed" not found', 'warn');
            }
            if (!qualityItem) {
                log('"Quality" not found', 'warn');
            }
        }
    }

    // MutationObserver to watch for changes in the DOM
    const observer = new MutationObserver((mutations, obs) => {
        // Check if the settings menu is visible
        const settingsMenu = document.querySelector('.ytp-panel-menu');
        const isMenuVisible = settingsMenu && settingsMenu.offsetParent !== null;

        if (isMenuVisible && !rearranged) {
            log('Settings menu opened');
            // Add a slight delay to ensure the menu is fully loaded
            setTimeout(() => {
                rearrangeSettingsMenu();
            }, 200); // Delay in milliseconds
        }

        if (!isMenuVisible && rearranged) {
            log('Settings menu closed');
            rearranged = false; // Reset the flag when the menu is closed
        }
    });

    // Start observing the document body for changes
    observer.observe(document.body, { childList: true, subtree: true });
    log('Script activated and observing DOM changes');

})();