🎉 欢迎访问GreasyFork.Org 镜像站!本镜像站由公众号【爱吃馍】搭建,用于分享脚本。联系邮箱📮

Greasy fork 爱吃馍镜像

YouTube Comment to Twitter

Adds a button to YouTube watch pages to easily tweet a comment with the video link.

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

You will need to install an extension such as Tampermonkey to install this script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Necesitará instalar una extensión como Tampermonkey para instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

🚀 安装遇到问题?关注公众号获取帮助

公众号二维码

扫码关注【爱吃馍】

回复【脚本】获取最新教程和防失联地址

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

🚀 安装遇到问题?关注公众号获取帮助

公众号二维码

扫码关注【爱吃馍】

回复【脚本】获取最新教程和防失联地址

// ==UserScript==
// @name         YouTube Comment to Twitter
// @namespace    http://tampermonkey.net/
// @version      1.3
// @description  Adds a button to YouTube watch pages to easily tweet a comment with the video link.
// @author       torch
// @match        *://www.youtube.com/watch*
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @run-at       document-end
// @license MIT
// ==/UserScript==

(function() {
    'use_strict';

    // --- Configuration ---
    const BUTTON_TEXT = "🐦 Твитнуть комментарий";
    const POPUP_TITLE = "Написать твит о видео";
    const TWITTER_MAX_LENGTH = 280; // Standard Twitter limit
    const TWITTER_URL_LENGTH = 23; // Standard length consumed by a t.co URL

    // --- Styles ---
    GM_addStyle(`
        #yt-comment-to-twitter-btn {
            background-color: #1DA1F2;
            color: white;
            border: none;
            padding: 8px 12px;
            text-align: center;
            text-decoration: none;
            display: inline-block;
            font-size: 14px;
            margin: 4px 2px;
            cursor: pointer;
            border-radius: 20px;
            font-weight: bold;
        }
        #yt-comment-to-twitter-btn:hover {
            background-color: #0c85d0;
        }
        .twitter-popup-overlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0, 0, 0, 0.5);
            z-index: 9998;
            display: flex;
            justify-content: center;
            align-items: center;
        }
        .twitter-popup-content {
            background-color: #1e1e1e; /* Darker theme for YouTube dark mode */
            color: #e0e0e0;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 0 15px rgba(0, 0, 0, 0.3);
            width: 400px;
            max-width: 90%;
            z-index: 9999;
        }
        .twitter-popup-content h3 {
            margin-top: 0;
            color: #1DA1F2;
        }
        .twitter-popup-content textarea {
            width: calc(100% - 20px);
            height: 100px;
            margin-bottom: 10px;
            padding: 10px;
            border: 1px solid #555;
            border-radius: 4px;
            background-color: #2a2a2a;
            color: #e0e0e0;
            resize: vertical;
        }
        .twitter-popup-content .char-counter {
            text-align: right;
            font-size: 0.9em;
            color: #aaa;
            margin-bottom: 10px;
        }
        .twitter-popup-content button {
            padding: 10px 15px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-weight: bold;
            margin-right: 10px;
        }
        .twitter-popup-content .tweet-btn {
            background-color: #1DA1F2;
            color: white;
        }
        .twitter-popup-content .tweet-btn:hover {
            background-color: #0c85d0;
        }
        .twitter-popup-content .cancel-btn {
            background-color: #555;
            color: white;
        }
        .twitter-popup-content .cancel-btn:hover {
            background-color: #777;
        }
    `);

    let popupOverlay = null;

    // getVideoTitle is not needed for the tweet content anymore, but kept in case it's useful for other features later.
    // function getVideoTitle() {
    //     const titleElement = document.querySelector('h1.ytd-video-primary-info-renderer yt-formatted-string, h1.title.ytd-video-primary-info-renderer');
    //     return titleElement ? titleElement.textContent.trim() : "YouTube Video";
    // }

    function getVideoUrl() {
        return window.location.href;
    }

    function createPopup() {
        if (popupOverlay) {
            popupOverlay.style.display = 'flex'; // Show if already created
            if (popupOverlay.querySelector('textarea')) {
                popupOverlay.querySelector('textarea').focus();
            }
            return;
        }

        popupOverlay = document.createElement('div');
        popupOverlay.className = 'twitter-popup-overlay';
        popupOverlay.onclick = function(e) {
            if (e.target === popupOverlay) {
                closePopup();
            }
        };

        const popupContent = document.createElement('div');
        popupContent.className = 'twitter-popup-content';

        const title = document.createElement('h3');
        title.textContent = POPUP_TITLE;

        const textarea = document.createElement('textarea');
        textarea.placeholder = "Ваш комментарий...";

        const charCounter = document.createElement('div');
        charCounter.className = 'char-counter';
        const updateCharCounter = () => {
            // The tweet will consist of the comment, a space, and the URL.
            // Twitter uses t.co to shorten URLs, which takes up a fixed number of characters.
            const lengthOfComment = textarea.value.length;
            const lengthOfSpaceAndUrl = (lengthOfComment > 0 ? 1 : 0) + TWITTER_URL_LENGTH; // Add space only if comment exists
            const remaining = TWITTER_MAX_LENGTH - lengthOfComment - lengthOfSpaceAndUrl;
            charCounter.textContent = `${remaining} символов осталось`;
            charCounter.style.color = remaining < 0 ? 'red' : '#aaa';
        };
        textarea.addEventListener('input', updateCharCounter);


        const tweetButton = document.createElement('button');
        tweetButton.textContent = "Твитнуть";
        tweetButton.className = 'tweet-btn';
        tweetButton.onclick = function() {
            const comment = textarea.value.trim();
            // Comment can be empty, in which case only the URL is tweeted via the 'url' parameter.
            // Twitter usually pre-fills the text field with the URL if the text parameter is empty.

            const videoUrl = getVideoUrl();

            // Construct tweet text: only the comment.
            // The videoUrl will be passed in the 'url' parameter of the Twitter intent.
            // Twitter will append the URL to the comment text.
            let tweetText = comment;

            const twitterIntentUrl = `https://twitter.com/intent/tweet?text=${encodeURIComponent(tweetText)}&url=${encodeURIComponent(videoUrl)}`;

            window.open(twitterIntentUrl, '_blank');
            closePopup();
        };

        const cancelButton = document.createElement('button');
        cancelButton.textContent = "Отмена";
        cancelButton.className = 'cancel-btn';
        cancelButton.onclick = closePopup;

        popupContent.appendChild(title);
        popupContent.appendChild(textarea);
        popupContent.appendChild(charCounter);
        popupContent.appendChild(tweetButton);
        popupContent.appendChild(cancelButton);
        popupOverlay.appendChild(popupContent);
        document.body.appendChild(popupOverlay);

        // Initialize counter
        updateCharCounter();
        textarea.focus();
    }

    function closePopup() {
        if (popupOverlay) {
            // Clear textarea for next time
            const textarea = popupOverlay.querySelector('textarea');
            if (textarea) {
                textarea.value = '';
            }
            popupOverlay.style.display = 'none';
        }
    }

    function addButton() {
        // More robust selectors for YouTube's dynamic layout
        const commonActionSelectors = [
            '#actions-inner #menu', // Older layout under video
            '#menu-container.ytd-watch-metadata', // Older layout alternative
            'ytd-video-actions #actions', // Newer layout for like/dislike etc.
            '#actions.ytd-watch-flexy' // Common actions row
        ];
        const fallbackSelectors = [
            '#info-contents #top-row.ytd-watch-info-text',
            '#meta-contents #info-contents',
            '#meta-contents #info',
            '#owner #subscribe-button' // As a last resort, place it near subscribe
        ];

        let actionsContainer = null;
        for (const selector of commonActionSelectors) {
            actionsContainer = document.querySelector(selector);
            if (actionsContainer) break;
        }

        if (!actionsContainer) {
            for (const selector of fallbackSelectors) {
                actionsContainer = document.querySelector(selector);
                if (actionsContainer) break;
            }
        }

        if (actionsContainer) {
            if (actionsContainer.querySelector('#yt-comment-to-twitter-btn')) {
                return; // Button already exists
            }

            const twitterButton = document.createElement('button');
            twitterButton.id = 'yt-comment-to-twitter-btn';
            twitterButton.textContent = BUTTON_TEXT;
            twitterButton.onclick = createPopup;

            // Attempt to insert it in a reasonable place
            if (actionsContainer.id === 'actions' && actionsContainer.parentElement?.tagName === 'YTD-VIDEO-ACTIONS') {
                // Preferred: Add next to like/share buttons
                 actionsContainer.insertBefore(twitterButton, actionsContainer.children[Math.min(2, actionsContainer.children.length)]);
            } else if (actionsContainer.firstChild) {
                 actionsContainer.insertBefore(twitterButton, actionsContainer.firstChild.nextSibling);
            } else {
                actionsContainer.appendChild(twitterButton);
            }
            // console.log("YouTube Comment to Twitter button added to:", actionsContainer);
        } else {
            // console.warn("Could not find a suitable container for the Twitter button after multiple attempts.");
        }
    }

    // YouTube uses dynamic loading, so we need to observe DOM changes
    function observeDOM() {
        const targetNode = document.body;
        const config = { childList: true, subtree: true };
        let lastPathname = window.location.pathname;
        let debounceTimer;

        const handleMutation = () => {
            // Try to add the button if it's not there
            if (!document.querySelector('#yt-comment-to-twitter-btn')) {
                addButton();
            }
        };

        const callback = function(mutationsList, observer) {
            // Check if navigation has happened to a new watch page
            if (window.location.pathname !== lastPathname && window.location.pathname.includes("/watch")) {
                lastPathname = window.location.pathname;
                // Wait a bit for the new page to load elements
                clearTimeout(debounceTimer);
                debounceTimer = setTimeout(handleMutation, 1000);
                return;
            }

            // General check for dynamic content loading on the current page
            for (const mutation of mutationsList) {
                if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                    // Check if a potential container is now available
                    if (document.querySelector('#actions-inner #menu, #menu-container.ytd-watch-metadata, ytd-video-actions #actions, #actions.ytd-watch-flexy') && !document.querySelector('#yt-comment-to-twitter-btn')) {
                        clearTimeout(debounceTimer);
                        debounceTimer = setTimeout(handleMutation, 300); // Debounce to avoid multiple rapid adds
                        break;
                    }
                }
            }
        };

        const observer = new MutationObserver(callback);
        observer.observe(targetNode, config);

        // Initial attempt in case the element is already there
        if (window.location.pathname.includes("/watch")) {
           setTimeout(addButton, 1000); // Initial delay for page load
        }
    }

    // Make sure the script runs after the page is mostly loaded
    if (document.readyState === "complete" || document.readyState === "interactive") {
        observeDOM();
    } else {
        window.addEventListener('DOMContentLoaded', observeDOM);
    }

})();