Jump to content

Recommended Posts

Posted

YouTube 13 - HD Auto Quality

// ==UserScript==
// @name - YouTube 13 - HD Auto Quality
// @version 1.0.2.1
// @match *://www.youtube.com/*
// @match *://m.youtube.com/*
// @match *://www.youtube-nocookie.com/*
// @exclude *://www.youtube.com/live_chat*
// @grant none
// @run-at document-idle
// ==/UserScript==

(async function () {
    'use strict';

    const QUALITIES = {
        highres: 4320,
        hd2160: 2160,
        hd1440: 1440,
        hd1080: 1080,
        hd720: 720,
        large: 480,
        medium: 360,
        small: 240,
        tiny: 144
    };

    const PREMIUM_INDICATOR_LABEL = "Premium";

    // Global variables
    let moviePlayer = null;
    let isIframe = false;

    async function setResolution() {
        if (!moviePlayer) {
            console.log('Movie player not found.');
            return;
        }

        // Debounce to prevent rapid calls
        let debounceTimer;
        if (debounceTimer) clearTimeout(debounceTimer);
        debounceTimer = setTimeout(async () => {
            try {
                const videoQualityData = moviePlayer.getAvailableQualityData();
                if (!videoQualityData.length) {
                    console.log('No quality data available, waiting for playback...');
                    const videoElement = moviePlayer.querySelector('video');
                    if (videoElement && videoElement.paused) {
                        videoElement.addEventListener('play', setResolution, { once: true });
                    }
                    return;
                }

                const availableQualities = moviePlayer.getAvailableQualityLevels();
                const highestQuality = availableQualities.reduce((max, q) => QUALITIES[q] > QUALITIES[max] ? q : max, availableQualities[0]);
                const premiumData = videoQualityData.find(q =>
                    q.quality === highestQuality &&
                    q.qualityLabel?.trim().endsWith(PREMIUM_INDICATOR_LABEL) &&
                    q.isPlayable
                );

                moviePlayer.setPlaybackQualityRange(highestQuality, highestQuality, premiumData?.formatId);
                console.log(`Set quality to: [${highestQuality}${premiumData ? ' Premium' : ''}]`);
            } catch (error) {
                console.error('Failed to set resolution:', error);
            }
        }, 100);
    }

    function processVideoLoad(event = null) {
        console.log('Processing video load...');
        moviePlayer = event?.target?.player_ ?? document.querySelector('#movie_player');
        if (moviePlayer) setResolution();
    }

    function addEventListeners() {
        if (window.location.hostname === 'm.youtube.com') {
            window.addEventListener('state-navigateend', processVideoLoad, true);
        } else {
            window.addEventListener('yt-player-updated', processVideoLoad, true);
        }
    }

    async function initialize() {
        if (window.self !== window.top) isIframe = true;
        window.addEventListener('pageshow', processVideoLoad, true);
        addEventListeners();
    }

    function hasGreasyMonkeyAPI() {
        return typeof GM !== 'undefined' || typeof GM_info !== 'undefined';
    }

    if (hasGreasyMonkeyAPI()) {
        initialize();
    } else {
        console.error('No Grease Monkey API detected.');
    }
})();


Posted

YouTube 14 - Advanced YouTube Search

// ==UserScript==
// @name - YouTube 14 - Advanced YouTube Search
// @version 1.1.1
// @match https://www.youtube.com/*
// @grant none
// @run-at document-end
// ==/UserScript==

(function() {
    'use strict';

    // Wait until the YouTube page is fully loaded
    function waitForElement(selector, callback) {
        const observer = new MutationObserver((mutations, me) => {
            const element = document.querySelector(selector);
            if (element) {
                me.disconnect();
                callback(element);
            }
        });
        observer.observe(document.body, { childList: true, subtree: true });
    }

    // Mapping for video length to 'sp' parameter
    const videoLengthOptions = {
        "any": "",
        "short": "EgIYAQ%253D%253D",      // Under 4 minutes
        "medium": "EgIYAw%253D%253D",     // 4-20 minutes
        "long": "EgIYAg%253D%253D"        // Over 20 minutes
    };

    // Create and inject the toggle button and advanced search popup
    function injectAdvancedSearchPopup() {
        // Create toggle button
        const toggleButton = document.createElement('button');
        toggleButton.innerText = 'Advanced Search';
        toggleButton.style.position = 'fixed';
        toggleButton.style.top = '16px';
        toggleButton.style.right = '20px';
        toggleButton.style.zIndex = '10000';
        toggleButton.style.padding = '11px 15px';
        toggleButton.style.backgroundColor = '#fff';
        toggleButton.style.color = '#333';
        toggleButton.style.border = '1px solid #c6c6c6 ';
        //toggleButton.style.borderRadius = '4px';
        toggleButton.style.cursor = 'pointer';
        //toggleButton.style.boxShadow = '0 2px 6px rgba(0,0,0,0.3)';
        toggleButton.style.fontSize = '14px';
        toggleButton.style.fontWeight = 'bold';
        //toggleButton.title = 'Toggle Advanced YouTube Search';

        document.body.appendChild(toggleButton);

        // Create the popup container
        const popupDiv = document.createElement('div');
        popupDiv.id = 'advancedSearchPopup';
        popupDiv.style.position = 'fixed';
        popupDiv.style.top = '65px';
        popupDiv.style.left = 'calc(100% - (952px / 2) - 20px)';
        popupDiv.style.transform = 'translateX(-50%)';
        popupDiv.style.width = '952px';
        popupDiv.style.backgroundColor = '#f9f9f9';
        popupDiv.style.border = '1px solid #c6c6c6';
        //popupDiv.style.borderRadius = '8px';
        //popupDiv.style.boxShadow = '0 4px 12px rgba(0,0,0,0.3)';
        popupDiv.style.padding = '0';
        popupDiv.style.zIndex = '10001';
        popupDiv.style.display = 'none'; // Initially hidden
        popupDiv.style.cursor = 'default';

        // Create the popup header for dragging
        const popupHeader = document.createElement('div');
        popupHeader.style.backgroundColor = '#FF0000';
        popupHeader.style.color = '#fff';
        popupHeader.style.padding = '2px';
        popupHeader.style.cursor = 'move';
        //popupHeader.style.borderTopLeftRadius = '8px';
        //popupHeader.style.borderTopRightRadius = '8px';
        popupHeader.style.fontSize = '14px';
        popupHeader.style.fontWeight = 'bold';
        popupHeader.style.textAlign = 'center';
        popupHeader.innerText = 'Advanced YouTube Search';

        popupDiv.appendChild(popupHeader);

        // Create the form container
        const formContainer = document.createElement('div');
        formContainer.style.padding = '4px';
        popupDiv.appendChild(formContainer);

        // Create form element
        const form = document.createElement('form');

        // Helper function to create form fields
        function createField(labelText, inputType, inputName, placeholder='', options=[]) {
            const div = document.createElement('div');
            div.style.marginBottom = '4px';

            const label = document.createElement('label');
            label.innerText = labelText;
            label.htmlFor = inputName;
            label.style.display = 'block';
            //label.style.marginBottom = '2px';
            label.style.fontWeight = '400';
            label.style.fontSize = '10px';

            let input;
            if (inputType === 'select') {
                input = document.createElement('select');
                input.name = inputName;
                input.id = inputName;

                // Add options based on select options provided
                options.forEach(option => {
                    const opt = document.createElement('option');
                    opt.value = option.value;
                    opt.text = option.text;
                    input.appendChild(opt);
                });
            } else if (inputType === 'date') {
                input = document.createElement('input');
                input.type = 'date';
                input.name = inputName;
                input.id = inputName;
            } else {
                input = document.createElement('input');
                input.type = inputType;
                input.name = inputName;
                input.id = inputName;
                input.placeholder = placeholder;
            }

            input.style.width = '100%';
            input.style.padding = '4px';
            input.style.boxSizing = 'border-box';
            input.style.border = '1px solid #ccc';
            input.style.borderRadius = '4px';
            input.style.textAlign = 'center';

            div.appendChild(label);
            div.appendChild(input);
            return div;
        }

        // === Regular Search Section ===
        const regularSearchSection = document.createElement('div');
        //regularSearchSection.style.marginBottom = '20px';
        regularSearchSection.style.textAlign = 'center';

        //const regularSearchTitle = document.createElement('h3');
        //regularSearchTitle.innerText = 'Regular Search';
        //regularSearchTitle.style.marginTop = '0';
        //regularSearchTitle.style.marginBottom = '10px';
        //regularSearchTitle.style.fontSize = '18px';
        //regularSearchTitle.style.color = '#333';

        //regularSearchSection.appendChild(regularSearchTitle);

        const regularSearchInput = createField('Search', 'text', 'regularSearch', '');

        regularSearchSection.appendChild(regularSearchInput);

        form.appendChild(regularSearchSection);

        // === Search List ===
        const searchList1a = document.createElement('li');
        searchList1a.innerHTML = '1955<br>1956<br>1957<br>1958<br>1959<br>1960<br>1961<br>1962<br>1963';
        searchList1a.style.display = 'inline-flex';
        searchList1a.style.padding = '0 12px';
        searchList1a.style.whiteSpace = 'pre-wrap';
        searchList1a.style.textAlign = 'center';
        searchList1a.style.fontSize = '11px';
        searchList1a.style.marginBottom = '4px';
        form.appendChild(searchList1a);

        const searchList1b = document.createElement('li');
        searchList1b.innerHTML = '1964<br>1965<br>1966<br>1967<br>1968<br>1969<br>1970<br>1971<br>1972';
        searchList1b.style.display = 'inline-flex';
        searchList1b.style.padding = '0 12px';
        searchList1b.style.whiteSpace = 'pre-wrap';
        searchList1b.style.textAlign = 'center';
        searchList1b.style.fontSize = '11px';
        searchList1b.style.marginBottom = '4px';
        form.appendChild(searchList1b);

        const searchList1c = document.createElement('li');
        searchList1c.innerHTML = '1973<br>1974<br>1975<br>1976<br>1977<br>1978<br>1979<br>1980<br>1981';
        searchList1c.style.display = 'inline-flex';
        searchList1c.style.padding = '0 12px';
        searchList1c.style.whiteSpace = 'pre-wrap';
        searchList1c.style.textAlign = 'center';
        searchList1c.style.fontSize = '11px';
        searchList1c.style.marginBottom = '4px';
        form.appendChild(searchList1c);

        const searchList1d = document.createElement('li');
        searchList1d.innerHTML = '1982<br>1983<br>1984<br>1985<br>1986<br>1987<br>1988<br>1989<br>1990';
        searchList1d.style.display = 'inline-flex';
        searchList1d.style.padding = '0 12px';
        searchList1d.style.whiteSpace = 'pre-wrap';
        searchList1d.style.textAlign = 'center';
        searchList1d.style.fontSize = '11px';
        searchList1d.style.marginBottom = '4px';
        form.appendChild(searchList1d);

        const searchList1e = document.createElement('li');
        searchList1e.innerHTML = '1991<br>1992<br>1993<br>1994<br>1995<br>1996<br>1997<br>1998<br>1999';
        searchList1e.style.display = 'inline-flex';
        searchList1e.style.padding = '0 12px';
        searchList1e.style.whiteSpace = 'pre-wrap';
        searchList1e.style.textAlign = 'center';
        searchList1e.style.fontSize = '11px';
        searchList1e.style.marginBottom = '4px';
        form.appendChild(searchList1e);

        const searchList1f = document.createElement('li');
        searchList1f.innerHTML = '2000<br>2001<br>2002<br>2003<br>2004<br>2005<br>2006<br>2007<br>2008';
        searchList1f.style.display = 'inline-flex';
        searchList1f.style.padding = '0 12px';
        searchList1f.style.whiteSpace = 'pre-wrap';
        searchList1f.style.textAlign = 'center';
        searchList1f.style.fontSize = '11px';
        searchList1f.style.marginBottom = '4px';
        form.appendChild(searchList1f);

        const searchList1g = document.createElement('li');
        searchList1g.innerHTML = '2009<br>2010<br>2011<br>2012<br>2013<br>2014<br>2015<br>2016<br>2017';
        searchList1g.style.display = 'inline-flex';
        searchList1g.style.padding = '0 12px';
        searchList1g.style.whiteSpace = 'pre-wrap';
        searchList1g.style.textAlign = 'center';
        searchList1g.style.fontSize = '11px';
        searchList1g.style.marginBottom = '4px';
        form.appendChild(searchList1g);

        const searchList1h = document.createElement('li');
        searchList1h.innerHTML = '2018<br>2019<br>2020<br>2021<br>2022<br>2023<br>2024<br>2025<br>2026';
        searchList1h.style.display = 'inline-flex';
        searchList1h.style.padding = '0 12px';
        searchList1h.style.whiteSpace = 'pre-wrap';
        searchList1h.style.textAlign = 'center';
        searchList1h.style.fontSize = '11px';
        searchList1h.style.marginBottom = '4px';
        form.appendChild(searchList1h);

        const searchList1 = document.createElement('li');
        searchList1.innerHTML = 'full movie<br>full sitcom episodes<br>documentary<br>gothic movies<br>noir film<br>noir tech<br>noir supernatural<br>"<br> ';
        searchList1.style.display = 'inline-flex';
        searchList1.style.padding = '0 12px';
        searchList1.style.whiteSpace = 'pre-wrap';
        searchList1.style.textAlign = 'center';
        searchList1.style.fontSize = '11px';
        searchList1.style.marginBottom = '4px';
        form.appendChild(searchList1);

        const searchList2 = document.createElement('li');
        searchList2.innerHTML = 'action<br>adventure<br>animation<br>comedy<br>crime<br>cult classic<br>detective<br>drama<br>family';
        searchList2.style.display = 'inline-flex';
        searchList2.style.padding = '0 12px';
        searchList2.style.whiteSpace = 'pre-wrap';
        searchList2.style.textAlign = 'center';
        searchList2.style.fontSize = '11px';
        searchList2.style.marginBottom = '4px';
        form.appendChild(searchList2);

        const searchList3 = document.createElement('li');
        searchList3.innerHTML = 'fantasy<br>haunted<br>holiday<br>horror<br>medieval<br>murder<br>mystery<br>mythology<br>psychological thriller';
        searchList3.style.display = 'inline-flex';
        searchList3.style.padding = '0 12px';
        searchList3.style.whiteSpace = 'pre-wrap';
        searchList3.style.textAlign = 'center';
        searchList3.style.fontSize = '11px';
        searchList3.style.marginBottom = '4px';
        form.appendChild(searchList3);

        const searchList4 = document.createElement('li');
        searchList4.innerHTML = 'romance<br>romantic comedy<br>supernatural<br>suspense<br>time travel<br>thriller<br>sci-fi<br>science fiction<br>sport';
        searchList4.style.display = 'inline-flex';
        searchList4.style.padding = '0 12px';
        searchList4.style.whiteSpace = 'pre-wrap';
        searchList4.style.textAlign = 'center';
        searchList4.style.fontSize = '11px';
        searchList4.style.marginBottom = '4px';
        form.appendChild(searchList4);

        const searchList5 = document.createElement('li');
        searchList5.innerHTML = 'dragon<br>ghoul<br>mobster gangster<br>superhero<br>supervillan<br>vampire<br>werewolf<br>witch<br>,';
        searchList5.style.display = 'inline-flex';
        searchList5.style.padding = '0 12px';
        searchList5.style.whiteSpace = 'pre-wrap';
        searchList5.style.textAlign = 'center';
        searchList5.style.fontSize = '11px';
        searchList5.style.marginBottom = '4px';
        form.appendChild(searchList5);

        const searchList6 = document.createElement('li');
        searchList6.innerHTML = 'Walt Disney Studios<br>Walt Disney Pictures<br>20th Century Studios<br>Muppets<br>Pixar<br>Marvel<br>Lucasfilm<br>National Geographic';
        searchList6.style.display = 'inline-flex';
        searchList6.style.padding = '0 21px';
        searchList6.style.whiteSpace = 'pre-wrap';
        searchList6.style.textAlign = 'center';
        searchList6.style.fontSize = '11px';
        searchList6.style.marginBottom = '4px';
        form.appendChild(searchList6);

        const searchList7 = document.createElement('li');
        searchList7.innerHTML = 'Warner Bros. Entertainment<br>Warner Bros. Pictures<br>New Line Cinema<br>DC Studios<br>DC Entertainment<br>Turner Entertainment<br>HBO<br>Discovery';
        searchList7.style.display = 'inline-flex';
        searchList7.style.padding = '0 21px';
        searchList7.style.whiteSpace = 'pre-wrap';
        searchList7.style.textAlign = 'center';
        searchList7.style.fontSize = '11px';
        searchList7.style.marginBottom = '4px';
        form.appendChild(searchList7);

        const searchList8 = document.createElement('li');
        searchList8.innerHTML = 'Universal Entertainment<br>Universal Pictures<br>DreamWorks<br>NBC Universal<br>Amblin<br>Peacock<br>SkyShowtime<br>Fandango';
        searchList8.style.display = 'inline-flex';
        searchList8.style.padding = '0 21px';
        searchList8.style.whiteSpace = 'pre-wrap';
        searchList8.style.textAlign = 'center';
        searchList8.style.fontSize = '11px';
        searchList8.style.marginBottom = '4px';
        form.appendChild(searchList8);

        const searchList9 = document.createElement('li');
        searchList9.innerHTML = 'Sony Pictures<br>Columbia Pictures<br>TriStar Pictures<br>Screen Gems<br>Stage 6 Films<br>Imageworks';
        searchList9.style.display = 'inline-flex';
        searchList9.style.padding = '0 21px';
        searchList9.style.whiteSpace = 'pre-wrap';
        searchList9.style.textAlign = 'center';
        searchList9.style.fontSize = '11px';
        searchList9.style.marginBottom = '4px';
        form.appendChild(searchList9);

        const searchList10 = document.createElement('li');
        searchList10.innerHTML = 'Paramount Skydance Studios<br>Paramount Pictures<br>Nickelodeon<br>Miramax<br>Avatar Studios<br>SkyShowtime';
        searchList10.style.display = 'inline-flex';
        searchList10.style.padding = '0 21px';
        searchList10.style.whiteSpace = 'pre-wrap';
        searchList10.style.textAlign = 'center';
        searchList10.style.fontSize = '11px';
        searchList10.style.marginBottom = '4px';
        form.appendChild(searchList10);

        const searchList11 = document.createElement('li');
        searchList11.innerHTML = 'Movie Central<br>Movie Time<br>Movie Union<br>AI Trailer Cinema<br>Amazon Studios<br>Lionsgate Studios';
        searchList11.style.display = 'inline-flex';
        searchList11.style.padding = '0 21px';
        searchList11.style.whiteSpace = 'pre-wrap';
        searchList11.style.textAlign = 'center';
        searchList11.style.fontSize = '11px';
        searchList11.style.marginBottom = '4px';
        form.appendChild(searchList11);

        const searchList12 = document.createElement('div');
        searchList12.innerText = '"based on", "true story", interview, audiobook, review, hindi';
        searchList12.style.whiteSpace = 'pre-wrap';
        searchList12.style.textAlign = 'center';
        searchList12.style.fontSize = '11px';
        searchList12.style.marginBottom = '4px';
        form.appendChild(searchList12);

        // === Advanced Search Section 1 ===
        const advancedSearchSection1 = document.createElement('div');
        advancedSearchSection1.style.display = 'inline-grid';
        advancedSearchSection1.style.textAlign = 'center';
        advancedSearchSection1.style.gridTemplateColumns = 'auto auto';
        advancedSearchSection1.style.width = '100%';
        advancedSearchSection1.style.gap = '0 3px';

        //const advancedSearchTitle = document.createElement('h3');
        //advancedSearchTitle.innerText = 'Advanced Options';
        //advancedSearchTitle.style.marginTop = '0';
        //advancedSearchTitle.style.marginBottom = '10px';
        //advancedSearchTitle.style.fontSize = '18px';
        //advancedSearchTitle.style.color = '#333';

        //advancedSearchSection1.appendChild(advancedSearchTitle);

        // Exact Term
        advancedSearchSection1.appendChild(createField('Exact', 'text', 'exactTerm', ''));

        // Exclude Term
        advancedSearchSection1.appendChild(createField('Exclude', 'text', 'excludeTerms', ''));

        // Title Includes
        //advancedSearchSection1.appendChild(createField('Title', 'text', 'titleIncludes', ''));

        // === Advanced Search Section 2 ===
        const advancedSearchSection2 = document.createElement('div');
        advancedSearchSection2.style.display = 'inline-grid';
        advancedSearchSection2.style.textAlign = 'center';
        advancedSearchSection2.style.gridTemplateColumns = 'auto auto auto';
        advancedSearchSection2.style.width = '100%';
        advancedSearchSection2.style.gap = '0 3px';

        // Video Length
        advancedSearchSection2.appendChild(createField('Length', 'select', 'videoLength', '', [
            {value: 'long', text: 'over 20 minutes'},
            {value: 'medium', text: '4-20 minutes'},
            {value: 'short', text: 'under 4 minutes'},
            {value: 'any', text: 'any length'}
        ]));

        // Date After
        advancedSearchSection2.appendChild(createField('After', 'date', 'dateAfter'));

        // Date Before
        advancedSearchSection2.appendChild(createField('Before', 'date', 'dateBefore'));

        form.appendChild(advancedSearchSection1);
        form.appendChild(advancedSearchSection2);

        // Submit Button
        const submitBtn = document.createElement('button');
        submitBtn.type = 'submit';
        submitBtn.innerText = 'Search';
        submitBtn.style.padding = '2px';
        submitBtn.style.backgroundColor = '#FF0000';
        submitBtn.style.color = '#fff';
        submitBtn.style.border = 'none';
        submitBtn.style.borderRadius = '4px';
        submitBtn.style.cursor = 'pointer';
        submitBtn.style.fontSize = '14px';
        submitBtn.style.fontWeight = 'bold';
        submitBtn.style.width = '100%';
        //submitBtn.style.marginTop = '4px';

        form.appendChild(submitBtn);

        // Handle form submission
        form.addEventListener('submit', function(e) {
            e.preventDefault();

            // Get form values
            const regularSearch = document.getElementById('regularSearch').value.trim();
            const exactTerm = document.getElementById('exactTerm').value.trim();
            const excludeTerms = document.getElementById('excludeTerms').value.trim();
            //const titleIncludes = document.getElementById('titleIncludes').value.trim();
            const videoLength = document.getElementById('videoLength').value;
            const dateAfter = document.getElementById('dateAfter').value;
            const dateBefore = document.getElementById('dateBefore').value;

            let query = '';

            // Add regular search term
            if (regularSearch) {
                query += `${regularSearch}`;
            }

            // Add exact term
            if (exactTerm) {
                if (query.length > 0) query += ' ';
                query += `"${exactTerm}"`;
            }

            // Add exclude terms
            if (excludeTerms) {
                const excludes = excludeTerms.split(',').map(term => term.trim()).filter(term => term);
                excludes.forEach(term => {
                    query += ` -${term}`;
                });
            }

            //// Add title includes
            //if (titleIncludes) {
            //    const titles = titleIncludes.split(',').map(term => term.trim()).filter(term => term);
            //    titles.forEach(term => {
            //        query += ` intitle:${term}`;
            //    });
            //}

            // Add date filters
            if (dateAfter) {
                query += ` after:${dateAfter}`;
            }
            if (dateBefore) {
                query += ` before:${dateBefore}`;
            }

            // Encode the query
            const encodedQuery = encodeURIComponent(query.trim());

            // Construct the search URL
            let searchURL = `https://www.youtube.com/results?search_query=${encodedQuery}`;

            // Add video length filter if not 'any'
            if (videoLength !== 'any') {
                searchURL += `&sp=${videoLengthOptions[videoLength]}`;
            }

            // Redirect to the constructed search URL
            window.location.href = searchURL;
        });

        formContainer.appendChild(form);
        popupDiv.appendChild(formContainer);
        document.body.appendChild(popupDiv);

        // Toggle button functionality
        toggleButton.addEventListener('click', (e) => {
            e.stopPropagation(); // Prevent event from bubbling up
            if (popupDiv.style.display === 'none') {
                popupDiv.style.display = 'block';
            } else {
                popupDiv.style.display = 'none';
            }
        });

        // Prevent popup click from closing when clicking inside
        popupDiv.addEventListener('click', (e) => {
            e.stopPropagation();
        });

        // Implement dragging functionality
        let isDragging = false;
        let offsetX, offsetY;

        popupHeader.addEventListener('mousedown', (e) => {
            isDragging = true;
            const rect = popupDiv.getBoundingClientRect();
            offsetX = e.clientX - rect.left;
            offsetY = e.clientY - rect.top;
            document.addEventListener('mousemove', onMouseMove);
            document.addEventListener('mouseup', onMouseUp);
        });

        function onMouseMove(e) {
            if (isDragging) {
                let newLeft = e.clientX - offsetX;
                let newTop = e.clientY - offsetY;

                // Ensure the popup stays within the viewport
                const popupWidth = popupDiv.offsetWidth;
                const popupHeight = popupDiv.offsetHeight;
                const windowWidth = window.innerWidth;
                const windowHeight = window.innerHeight;

                if (newLeft < 0) newLeft = 0;
                if (newTop < 0) newTop = 0;
                if (newLeft + popupWidth > windowWidth) newLeft = windowWidth - popupWidth;
                if (newTop + popupHeight > windowHeight) newTop = windowHeight - popupHeight;

                popupDiv.style.left = `${newLeft}px`;
                popupDiv.style.top = `${newTop}px`;
                popupDiv.style.transform = 'none'; // Disable transform when dragging
            }
        }

        function onMouseUp() {
            isDragging = false;
            document.removeEventListener('mousemove', onMouseMove);
            document.removeEventListener('mouseup', onMouseUp);
        }

        //// Optional: Close popup when clicking outside of it
        //document.addEventListener('click', function(event) {
        //    if (!popupDiv.contains(event.target) && event.target !== toggleButton) {
        //        popupDiv.style.display = 'none';
        //    }
        //});

    }

    // Initialize the script by injecting the popup
    waitForElement('body', function() {
        injectAdvancedSearchPopup();
    });

})();

Posted

YouTube 15 - Search While Watching Video

// ==UserScript==
// @name - YouTube 15 - Search While Watching Video
// @version 2.5.5.1
// @match       https://www.youtube.com/*
// @require     https://cdn.jsdelivr.net/gh/culefa/JavaScript-autoComplete@19203f30f148e2d9d810ece292b987abb157bbe0/auto-complete.min.js
// @run-at       document-start
// @grant        none
// @noframes
// ==/UserScript==

(function() {
    'use strict';

    // Handle Trusted Type Policy Violations
    var TTP = window.TTP = {createHTML: (string, sink) => string, createScript: (string, sink) => string, createScriptURL: (string, sink) => string};
    if(typeof window.isSecureContext !== 'undefined' && window.isSecureContext){
        if (window.trustedTypes && window.trustedTypes.createPolicy){
            if(window.trustedTypes.defaultPolicy) {
                TTP = window.TTP = window.trustedTypes.defaultPolicy;
            } else {
                TTP = window.TTP = window.trustedTypes.createPolicy("default", TTP);
            }
        }
    }

    function youtube_search_while_watching_video() {
        let script = {
            initialized: false,

            ytplayer: null,

            search_bar: null,
            search_autocomplete: null,
            search_suggestions: [],
            searched: false,

            debug: false
        };

        const insp = o => o ? (o.polymerController || o.inst || o || 0) : (o || 0);

        document.addEventListener("DOMContentLoaded", initScript);

        // reload script on page change using youtube polymer fire events
        window.addEventListener("yt-page-data-updated", function(event) {
            if (script.debug) { console.log("# page updated #"); }
            cleanupSearch();
            startScript(2);
        });

        function initScript() {
            if (script.debug) { console.log("### Youtube Search While Watching Video Initializing ###"); }

            initSearch();
            injectCSS();

            if (script.debug) { console.log("### Youtube Search While Watching Video Initialized ###"); }
            script.initialized = true;

            startScript(5);
        }

        function startScript(retry) {
            if (script.initialized && isPlayerAvailable()) {
                if (script.debug) { console.log("videoplayer is available"); }
                if (script.debug) { console.log("ytplayer: ", script.ytplayer); }

                if (script.ytplayer) {
                    try {
                        if (script.debug) { console.log("initializing search"); }
                        loadSearch();
                    } catch (error) {
                        console.log("Failed to initialize search: ", (script.debug) ? error : error.message);
                    }
                }
            } else if (retry > 0) { // fix conflict with Youtube+ script
                setTimeout( function() {
                    startScript(--retry);
                }, 1000);
            } else {
                if (script.debug) { console.log("videoplayer is unavailable"); }
            }
        }

        // *** VIDEOPLAYER *** //

        function getVideoPlayer() {
            return insp(document.getElementById('movie_player'));
        }

        function isPlayerAvailable() {
            script.ytplayer = getVideoPlayer();
            return script.ytplayer !== null && script.ytplayer.getVideoData?.().video_id;
        }

        // *** SEARCH *** //

        function initSearch() {
            // callback function for search suggestion results
            window.suggestions_callback = suggestionsCallback;
        }

        function loadSearch() {
            // prevent double searchbar
            let playlistOrLiveSearchBar = document.querySelector('#suggestions-search.playlist-or-live');
            if (playlistOrLiveSearchBar) { playlistOrLiveSearchBar.remove(); }

            let searchbar = document.getElementById('suggestions-search');
            if (!searchbar) {
                createSearchBar();
            } else {
                searchbar.value = "";
            }

            script.searched = false;
            cleanupSuggestionRequests();
        }

        function cleanupSearch() {
            if (script.search_autocomplete) {
                script.search_autocomplete.destroy();
            }
        }

        function createSearchBar() {
            let anchor, html;

            anchor = document.querySelector('ytd-compact-autoplay-renderer > #contents');
            if (anchor) {
                //html = "<input id=\"suggestions-search\" type=\"search\" placeholder=\"Search\">";
                html = "<input id=\"suggestions-search\" type=\"search\">";
                anchor.insertAdjacentHTML("afterend", html);
            } else { // playlist, live video or experimental youtube layout (where autoplay is not a separate renderer anymore)
                anchor = document.querySelector('#related > ytd-watch-next-secondary-results-renderer');
                if (anchor) {
                    //html = "<input id=\"suggestions-search\" class=\"playlist-or-live\" type=\"search\" placeholder=\"Search\">";
                    html = "<input id=\"suggestions-search\" class=\"playlist-or-live\" type=\"search\">";
                    anchor.insertAdjacentHTML("beforebegin", html);
                }
            }

            let searchBar = document.getElementById('suggestions-search');
            if (searchBar) {
                script.search_bar = searchBar;

                script.search_autocomplete = new window.autoComplete({
                    selector: '#suggestions-search',
                    minChars: 1,
                    delay: 100,
                    source: function(term, suggest) {
                        script.search_suggestions = {
                            query: term,
                            suggest: suggest
                        };
                        searchSuggestions(term);
                    },
                    onSelect: function(event, term, item) {
                        prepareNewSearchRequest(term);
                    }
                });

                script.search_bar.addEventListener("keyup", function(event) {
                    if (this.value === "") {
                        resetSuggestions();
                    }
                });

                // seperate keydown listener because the search listener blocks keyup..?
                script.search_bar.addEventListener("keydown", function(event) {
                    const ENTER = 13;
                    if (this.value.trim() !== "" && (event.key == "Enter" || event.keyCode === ENTER)) {
                        prepareNewSearchRequest(this.value.trim());
                    }
                });

                script.search_bar.addEventListener("search", function(event) {
                    if(this.value === "") {
                        script.search_bar.blur(); // close search suggestions dropdown
                        script.search_suggestions = []; // clearing the search suggestions

                        resetSuggestions();
                    }
                });

                script.search_bar.addEventListener("focus", function(event) {
                    this.select();
                });
            }
        }

        // callback from search suggestions attached to window
        function suggestionsCallback(data) {
            if (script.debug) { console.log(data); }

            let query = data[0];
            if (query !== script.search_suggestions.query) {
                return;
            }

            let raw = data[1]; // extract relevant data from json
            let suggestions = raw.map(function(array) {
                return array[0]; // change 2D array to 1D array with only suggestions
            });

            script.search_suggestions.suggest(suggestions);
        }

        function searchSuggestions(query) {
            // youtube search parameters
            const GeoLocation = window.yt.config_.INNERTUBE_CONTEXT_GL;
            const HostLanguage = window.yt.config_.INNERTUBE_CONTEXT_HL;

            if (script.debug) { console.log("suggestion request send", query); }
            let scriptElement = document.createElement("script");
            scriptElement.type = "text/javascript";
            scriptElement.className = "suggestion-request";
            //scriptElement.src = "https://clients1.google.com/complete/search?client=youtube&hl=" + HostLanguage + "&gl=" + GeoLocation + "&gs_ri=youtube&ds=yt&q=" + encodeURIComponent(query) + "&callback=suggestions_callback";
            (document.body || document.head || document.documentElement).appendChild(scriptElement);
        }

        function cleanupSuggestionRequests() {
            let requests = document.getElementsByClassName('suggestion-request');
            forEachReverse(requests, function(request) {
                request.remove();
            });
        }

        // send new search request (with the search bar)
        function prepareNewSearchRequest(value) {
            if (script.debug) { console.log("searching for " + value); }

            script.search_bar.blur(); // close search suggestions dropdown
            script.search_suggestions = []; // clearing the search suggestions
            cleanupSuggestionRequests();

            sendSearchRequest("https://www.youtube.com/results?pbj=1&search_query=" + encodeURIComponent(value));
        }

        // given the url, retrieve the search results
        function sendSearchRequest(url) {
            let xmlHttp = new XMLHttpRequest();
            xmlHttp.onreadystatechange = function() {
                if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
                    processSearch(xmlHttp.responseText);
                }
            };

            xmlHttp.open("GET", url, true);
            xmlHttp.setRequestHeader("x-youtube-client-name", window.yt.config_.INNERTUBE_CONTEXT_CLIENT_NAME);
            xmlHttp.setRequestHeader("x-youtube-client-version", window.yt.config_.INNERTUBE_CONTEXT_CLIENT_VERSION);
            xmlHttp.setRequestHeader("x-youtube-client-utc-offset", new Date().getTimezoneOffset() * -1);

            if (window.yt.config_.ID_TOKEN) { // null if not logged in
                xmlHttp.setRequestHeader("x-youtube-identity-token", window.yt.config_.ID_TOKEN);
            }

            xmlHttp.send(null);
        }

        // process search request
        function processSearch(responseText) {
            try {
                let data = JSON.parse(responseText);

                let found = searchJson(data, (key, value) => {
                    if (key === "itemSectionRenderer") {
                        if (script.debug) { console.log(value.contents); }
                        let succeeded = createSuggestions(value.contents);
                        return succeeded;
                    }
                    return false;
                });

                if (!found) {
                    alert("The search request was succesful but the script was unable to parse the results");
                }
            } catch (error) {
                alert("Failed to retrieve search data, sorry!\nError message: " + error.message + "\nSearch response: " + responseText);
            }
        }

        function searchJson(json, func) {
            let found = false;

            for (let item in json) {
                found = func(item, json[item]);
                if (found) { break; }

                if (json[item] !== null && typeof(json[item]) == "object") {
                    found = searchJson(json[item], func);
                    if (found) { break; }
                }
            }

            return found;
        }

        // *** HTML & CSS *** //

        function createSuggestions(data) {
            // filter out promotional stuff
            if (data.length < 10) {
                return false;
            }

            // remove current suggestions
            let hidden_continuation_item_renderer;
            let watchRelated = document.querySelector('#related ytd-watch-next-secondary-results-renderer #items ytd-item-section-renderer #contents') || document.querySelector('#related ytd-watch-next-secondary-results-renderer #items');
            forEachReverse(watchRelated.children, function(item) {
                if (item.tagName === "YTD-CONTINUATION-ITEM-RENDERER") {
                    item.setAttribute("hidden", "");
                    hidden_continuation_item_renderer = item;
                } else if (item.tagName !== "YTD-COMPACT-AUTOPLAY-RENDERER") {
                    item.remove();
                }
            });

            // create suggestions
            forEach(data, function(videoData) {
                if (videoData.videoRenderer || videoData.compactVideoRenderer) {
                    window.Polymer.dom(watchRelated).appendChild(videoQueuePolymer(videoData.videoRenderer || videoData.compactVideoRenderer, "ytd-compact-video-renderer"));
                } else if (videoData.radioRenderer || videoData.compactRadioRenderer) {
                    window.Polymer.dom(watchRelated).appendChild(videoQueuePolymer(videoData.radioRenderer || videoData.compactRadioRenderer, "ytd-compact-radio-renderer"));
                } else if (videoData.playlistRenderer || videoData.compactPlaylistRenderer) {
                    window.Polymer.dom(watchRelated).appendChild(videoQueuePolymer(videoData.playlistRenderer || videoData.compactPlaylistRenderer, "ytd-compact-playlist-renderer"));
                }
            });

            if (hidden_continuation_item_renderer) {
                watchRelated.appendChild(hidden_continuation_item_renderer);
            }

            script.searched = true;

            return true;
        }

        function resetSuggestions() {
            if (script.searched) {
                let itemSectionRenderer = document.querySelector('#related ytd-watch-next-secondary-results-renderer #items ytd-item-section-renderer') || document.querySelector("#related ytd-watch-next-secondary-results-renderer");
                let data = insp(itemSectionRenderer).__data.data;
                createSuggestions(data.contents || data.results);

                // restore continuation renderer
                let continuation = itemSectionRenderer.querySelector('ytd-continuation-item-renderer[hidden]');
                if (continuation) {
                    continuation.removeAttribute("hidden");
                }
            }

            script.searched = false;
        }

        function videoQueuePolymer(videoData, type) {
            let node = document.createElement(type);
            node.classList.add("style-scope", "ytd-watch-next-secondary-results-renderer", "yt-search-generated");
            node.data = videoData;
            return node;
        }

        function injectCSS() {
            let css = `
.autocomplete-suggestions {
text-align: left; cursor: default; border: 1px solid var(--ytd-searchbox-legacy-border-color); border-top: 0; background: var(--ytd-searchbox-background);
position: absolute; /*display: none; z-index: 9999;*/ max-height: 254px; overflow: hidden; overflow-y: auto; box-sizing: border-box; box-shadow: -1px 1px 3px rgba(0,0,0,.1);
    left: auto; top: auto; width: 100%; margin: 0; contain: content; /* 1.2.0 */
}
.autocomplete-suggestion { position: relative; padding: 0 .6em; line-height: 23px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 1.22em; color: var(--ytd-searchbox-text-color); }
.autocomplete-suggestion b { font-weight: normal; color: #b31217; }
.autocomplete-suggestion.selected { background: #ddd; }
[dark] .autocomplete-suggestion.selected { background: #333; }

autocomplete-holder {
    overflow: visible; position: absolute; left: auto; top: auto; width: 100%; height: 0; z-index: 9999; box-sizing: border-box; margin:0; padding:0; border:0; contain: size layout;
}

ytd-compact-autoplay-renderer { padding-bottom: 0px; }

#suggestions-search {
outline: none; width: 100%; padding: 6px 5px; margin-bottom: 16px;
border: 1px solid var(--ytd-searchbox-legacy-border-color); border-radius: 2px 0 0 2px;
box-shadow: inset 0 1px 2px var(--ytd-searchbox-legacy-border-shadow-color);
color: var(--ytd-searchbox-text-color); background-color: var(--ytd-searchbox-background);
}
`;

            let style = document.createElement("style");
            style.type = "text/css";
            if (style.styleSheet){
                style.styleSheet.cssText = css;
            } else {
                style.appendChild(document.createTextNode(css));
            }

            (document.body || document.head || document.documentElement).appendChild(style);
        }

        // *** FUNCTIONALITY *** //

        function forEach(array, callback, scope) {
            for (let i = 0; i < array.length; i++) {
                callback.call(scope, array, i);
            }
        }

        // When you want to remove elements
        function forEachReverse(array, callback, scope) {
            for (let i = array.length - 1; i >= 0; i--) {
                callback.call(scope, array, i);
            }
        }
    }

    // ================================================================================= //
    // =============================== INJECTING SCRIPTS =============================== //
    // ================================================================================= //

    document.documentElement.setAttribute("youtube-search-while-watching-video", "");

    if (!document.getElementById("autocomplete_script")) {
        let autoCompleteScript = document.createElement('script');
        autoCompleteScript.id = "autocomplete_script";
        autoCompleteScript.type = 'text/javascript';
        autoCompleteScript.textContent = 'window.autoComplete = ' + autoComplete + ';';
        (document.body || document.head || document.documentElement).appendChild(autoCompleteScript);
    }

    if (!document.getElementById("search_while_watching_video")) {
        let searchWhileWatchingVideoScript = document.createElement('script');
        searchWhileWatchingVideoScript.id = "search_while_watching_video";
        searchWhileWatchingVideoScript.type = 'text/javascript';
        searchWhileWatchingVideoScript.textContent = '('+ youtube_search_while_watching_video +')();';
        (document.body || document.head || document.documentElement).appendChild(searchWhileWatchingVideoScript);
    }
})();

Posted

Userstyle:

/* YouTube  --  1/14/26 */

/* homepage / general */
* {
  --yt-d-rich-grid-items-per-row: 4;
  --yt-d-rich-grid-posts-per-row: 4;
  outline: none !important;
  transition: none !important;}
#buttons.style-scope.ytd-masthead {display: none !important;}
#container.ytd-masthead {
  display: block !important;
  margin: 16px 25% 0 calc(25% - 3px) !important;
  width: calc(50% + 20px + 52px - 17px - 64px) !important;}
#dismissible.ytd-video-renderer {margin: 24px 0 -12px 0 !important;}
#footer {display: none !important;}
#masthead-container.style-scope.ytd-app {
  background-color: #fff !important;
  height: 80px !important;}
#offer-module {display: none !important;}
#start.style-scope.ytd-masthead {display: none !important;}
#voice-search-button {display: none !important;}
.yt-content-metadata-view-model__badge {display: none !important;}
.ytp-spinner {display: none !important;}
.ytSearchboxComponentClearButtonWrapper {
  position: relative !important;
    left: calc(100% - 40px) !important;
    top: -25px !important;}
.ytSearchboxComponentHost {margin: 0 !important;}
.ytSearchboxComponentInnerSearchIcon {display: none !important;}
.ytSearchboxComponentInput {text-align: center !important;}
.ytSearchboxComponentInput::placeholder {color: transparent !important;}
.ytSearchboxComponentInputBox {
  border-radius: 0 !important;
  border: 1px #c6c6c6 solid !important;
  box-shadow: none !important;
  display: block !important;
  margin: 0 !important;
  padding: 8px 0 !important;}
.ytSearchboxComponentSearchButton {
  background-color: transparent !important;
  border-color: #c6c6c6 !important;
  border-left: none !important;
  border-radius: 0 !important;}
.ytSearchboxComponentSuggestionsContainer {display: none !important;}
ytd-guide-section-renderer {display: none !important;}
ytd-guide-signin-promo-renderer {display: none !important;}
ytd-mini-guide-entry-renderer {display: none !important;}
[id*='skeleton'] {opacity: 0 !important;}

/* remove rounded corners */
.ytCoreImageFillParentHeight {height: 101% !important;}
.ytThumbnailViewModelMedium, .ytd-thumbnail {border-radius: 0 !important;}
ytd-player {border-radius: 0 !important;}

/* search results filter header */
#header.ytd-search {display: none !important;}

/* normal player size and position */
#player-container-outer {
  position: fixed !important;
  width: calc(100% - 450px - 20px) !important;}

/* windowed fullscreen search */
body.updated-full-mode #masthead-container {display: none !important;}

/* free-with-ads thumbnail description */
ytd-badge-supported-renderer {display: none !important;}

/* hide player controls until hover */
.ytp-chrome-bottom {opacity: 0 !important;}
.ytp-chrome-bottom:hover {opacity: 1 !important;}

/* hide play/pause icon */
.ytp-bezel-text-hide {display: none !important;}

/* progress bar */
.ytp-hover-progress-light {background: unset !important;}
.ytp-load-progress {background: unset !important;}
.ytp-play-progress {background: #f03 !important;}
.ytp-progress-bar-container {
  height: 6px !important;
  top: 0 !important;}
.ytp-progress-bar-container:hover {
  height: calc(6px / 1.6) !important;
  margin-top: 1px !important;}
.ytp-scrubber-container {display: none !important;}

/* closed captions */
.caption-visual-line {text-align: center !important;}
.ytp-caption-window-container .caption-window.ytp-caption-window-bottom:nth-child(1):has(.caption-visual-line:nth-child(2)) {margin-top: -38px !important;}
.ytp-caption-window-container .caption-window:not(.ytp-caption-window-bottom) {
  margin-left: 0 !important;
  position: absolute !important;
    left: 0 !important;
    top: calc(80% + (38px / 2)) !important;
  width: 100% !important;}
.ytp-caption-window-container .caption-window.ytp-caption-window-bottom:nth-child(1) {
  cursor: default !important;
  margin-left: 0 !important;
  position: absolute !important;
    left: 0 !important;
    top: 80% !important;
  width: 100% !important;}
.ytp-caption-window-container .caption-window.ytp-caption-window-bottom:nth-child(2) {
  cursor: default !important;
  margin-left: 0 !important;
  position: absolute !important;
    left: 0 !important;
    top: calc(80% + 38px) !important;
  width: 100% !important;}
.ytp-caption-window-container .caption-window.ytp-caption-window-bottom:nth-child(3) {
  cursor: default !important;
  margin-left: 0 !important;
  position: absolute !important;
    left: 0 !important;
    top: calc(80% + 38px + 38px) !important;
  width: 100% !important;}
.ytp-caption-segment  {
  background-color: rgba(0, 0, 0, 0.5) !important;
  color: rgb(200, 200, 50) !important;
  display: inline !important;
  font-family: sans-serif !important;
  font-size: 30px !important;
  font-weight: 600 !important;
  line-height: 38px !important;
  padding: 0 0 2px 0 !important;}


/* advanced search userscript additions */
button[style*='position'] {opacity: 0 !important;}
button[style*='position']:hover {opacity: 1 !important;}

/* search while watching userscript additions */
ytd-watch-next-secondary-results-renderer #items {margin-top: 12px !important;}
#suggestions-search {
  background-color: #fff !important;
  border: 1px solid #c6c6c6 !important;
  border-radius: 0 !important;
  opacity: 0 !important;
  outline: 10px solid white !important;
  position: fixed !important;
    top: 80px !important;
  text-align: center !important;
  width: var(--ytd-watch-flexy-sidebar-width) !important;
  z-index: 1000 !important;}
#suggestions-search:hover {opacity: 1 !important;}
@media screen and (max-width: 1350px) {
  #suggestions-search {width: 33.5% !important;}}

/* controllers userscript additions */
#ytControllerHost {opacity: 0 !important;}
#ytControllerHost:hover {opacity: 1 !important;}

/* userscript video controls overrides */
#sliderContainer:hover {height: 170px !important;}

Posted (edited)
7 hours ago, dmiranda said:

Currently I follow https://www.reddit.com/r/uBlockOrigin/wiki/solutions/youtube/?solution=30e785c7e8831bb230e785c7e8831bb2&js

But there are remaining issues. I just wanted to gather current knowledge on (for example):

Stop auto-pause

Prevent scrolling suggested videos (right panel on video) from getting caught in the first screen

 

ps - I read this as "How are others fixing these issues on YouTube?".  I do *everything* with userstyles and userscripts and *not* with uBO custom filters/lists.

Edited by NotHereToPlayGames
Posted (edited)

Thanks for your suggestions. I will try them at my own path. As you know, I left behind XP for linux mint a year and a half ago, when my old PC died after 15 years of excellent service. But many of the inconveniences in bloated sites (such as YT) remain surprisingly the same. That is why I ask here (I had you in mind when I asked, btw), because of the variety of experiences and the deep (difficult to find elsewhere) know-how. I shall revisit (virtualized) XP soon, as there are a number of purchased programs I have that only run in windows, because there are a few free (mostly FOSS) apps that are far superior in MS than in linux world, and because I simply like the speed and convenience of a fully tweaked XP (especially once, I think, I make the poor thing free of having to deal with today's internet bloat). Be well.  

Edited by dmiranda
Posted

Until I was on MSFN, I literally (and I do mean LITERALLY) **NEVER** visited YouTube.  ****NEVER****

I adblock, so I don't even know who advertises on MSFN, but I do know it is TERRIBLE here at MSFN if you do not adblock.  Okay, not "terrible", but a nuisance nonetheless.

I once witnessed a bra and panty ad here at MSFN while *AT WORK*, thank golly-G no coworkers walked into my office, they would have thought I was on a "smut page".

YouTube *should* ADVERTISE here on MSFN, since they seem to OWN every user herein.  From "YT downloaders" to "how do I make my 20yr old computer work on YT".

But in the couple of years that I "did" (past tense) use YouTube, it warranted 16 out of 53 site-specific userscripts.

1 for Invidious as a YouTube fallback and 15 for YouTube.  So let's call it 15 out of 53.  ie, just over 28% of all of my userscripts are for YouTube.

I probably should just ditch them all, lol, but who knows, I may use YouTube again one of these days.  And they did consume a lot of time to get YouTube to "tolerable".

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...