Advanced YouTube Search

Enhance YouTube search with both regular and advanced parameters like exact terms, exclusions, title includes, video length, and date range.

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Advanced YouTube Search
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  Enhance YouTube search with both regular and advanced parameters like exact terms, exclusions, title includes, video length, and date range.
// @author       bitgineer https://github.com/bitgineer
// @match        https://www.youtube.com/*
// @grant        none
// @run-at       document-end
// @license MIT
// ==/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.bottom = '20px';
        toggleButton.style.right = '20px';
        toggleButton.style.zIndex = '1000';
        toggleButton.style.padding = '10px 15px';
        toggleButton.style.backgroundColor = '#FF0000';
        toggleButton.style.color = '#fff';
        toggleButton.style.border = 'none';
        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 = '100px';
        popupDiv.style.left = '50%';
        popupDiv.style.transform = 'translateX(-50%)';
        popupDiv.style.width = '450px';
        popupDiv.style.backgroundColor = '#f9f9f9';
        popupDiv.style.border = '1px solid #ccc';
        popupDiv.style.borderRadius = '8px';
        popupDiv.style.boxShadow = '0 4px 12px rgba(0,0,0,0.3)';
        popupDiv.style.padding = '0';
        popupDiv.style.zIndex = '1001';
        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 = '10px';
        popupHeader.style.cursor = 'move';
        popupHeader.style.borderTopLeftRadius = '8px';
        popupHeader.style.borderTopRightRadius = '8px';
        popupHeader.style.fontSize = '16px';
        popupHeader.style.fontWeight = 'bold';
        popupHeader.innerText = 'Advanced YouTube Search';

        popupDiv.appendChild(popupHeader);

        // Create the form container
        const formContainer = document.createElement('div');
        formContainer.style.padding = '15px';
        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 = '12px';

            const label = document.createElement('label');
            label.innerText = labelText;
            label.htmlFor = inputName;
            label.style.display = 'block';
            label.style.marginBottom = '6px';
            label.style.fontWeight = 'bold';

            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 = '8px';
            input.style.boxSizing = 'border-box';
            input.style.border = '1px solid #ccc';
            input.style.borderRadius = '4px';

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

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

        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', 'e.g., funny cats');

        regularSearchSection.appendChild(regularSearchInput);

        form.appendChild(regularSearchSection);

        // === Advanced Search Section ===
        const advancedSearchSection = document.createElement('div');

        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';

        advancedSearchSection.appendChild(advancedSearchTitle);

        // Exact Term
        advancedSearchSection.appendChild(createField('Exact Term:', 'text', 'exactTerm', 'e.g., "Awesome List"'));

        // Exclude Term
        advancedSearchSection.appendChild(createField('Exclude Term(s):', 'text', 'excludeTerms', 'e.g., spam, ads'));

        // Title Includes
        advancedSearchSection.appendChild(createField('Title Includes:', 'text', 'titleIncludes', 'e.g., tutorial'));

        // Video Length
        advancedSearchSection.appendChild(createField('Video Length:', 'select', 'videoLength', '', [
            {value: 'any', text: 'Any Length'},
            {value: 'short', text: 'Under 4 minutes'},
            {value: 'medium', text: '4-20 minutes'},
            {value: 'long', text: 'Over 20 minutes'}
        ]));

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

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

        form.appendChild(advancedSearchSection);

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

        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();
    });

})();