YouTube Shorts URL Conversion Button

Convert YouTube Shorts URL to regular YouTube video URL.

Tính đến 21-10-2024. Xem phiên bản mới nhất.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

Bạn sẽ cần cài đặt một tiện ích mở rộng như Tampermonkey hoặc Violentmonkey để cài đặt kịch bản này.

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

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

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

You will need to install a user script manager extension to install this script.

(Tôi đã có Trình quản lý tập lệnh người dùng, hãy cài đặt nó!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name                YouTube Shorts URL Conversion Button
// @name:zh-TW          YouTube Shorts URL 轉換按鈕
// @name:zh-HK          YouTube Shorts URL 轉換按鈕
// @name:ja             YouTube Shorts URL コンバーター
// @name:zh-CN          YouTube Shorts URL 转换按钮
// @name:ko             YouTube Shorts URL 변환 버튼
// @name:ru             Кнопка преобразования URL YouTube Shorts
// @name:de             YouTube Shorts URL Konvertierungstaste
// @name:es             Botón de conversión de URL de YouTube Shorts
// @name:fr             Bouton de conversion d'URL YouTube Shorts
// @name:it             Pulsante di conversione URL YouTube Shorts
// @namespace           http://tampermonkey.net/
// @version             1.7
// @description         Convert YouTube Shorts URL to regular YouTube video URL.
// @description:zh-TW   將 YouTube Shorts 網址轉換為常規的 YouTube 影片網址。
// @description:zh-HK   將 YouTube Shorts 網址轉換為常規的 YouTube 影片網址。
// @description:ja      YouTube Shorts URLを通常のYouTubeビデオURLに変換します。
// @description:zh-CN   将 YouTube Shorts 网址转换为常规的 YouTube 视频网址。
// @description:ko      YouTube Shorts URL을 일반 YouTube 비디오 URL로 변환합니다.
// @description:ru      Преобразование URL YouTube Shorts в обычный URL видео YouTube.
// @description:de      Konvertiere YouTube Shorts URL in reguläre YouTube Video URL.
// @description:es      Convierte la URL de YouTube Shorts en una URL de video de YouTube normal.
// @description:fr      Convertir l'URL YouTube Shorts en URL vidéo YouTube classique.
// @description:it      Converti l'URL YouTube Shorts in URL video YouTube normale.
// @author              鮪魚大師
// @match               https://www.youtube.com/*
// @grant               none
// @license             MIT
// ==/UserScript==

(function() {
    'use strict';

    const debug = false;

    let convertButton;
    let isButtonDown = false;

    const lang = navigator.language;
    const langData = [
        {
            name: "English",
            match: ["en"],
            lang: {
                buttonText: "Convert Shorts",
                buttonTitle: "Convert Shorts URL to regular video URL",
            },
        },
        {
            name: "Chinese (Traditional)",
            match: ["zh-TW", "zh-HK"],
            lang: {
                buttonText: "Shorts轉換",
                buttonTitle: "將Shorts網址轉換成一般影片網址",
            },
        },
        {
            name: "Japanese",
            match: ["ja"],
            lang: {
                buttonText: "Shorts変換",
                buttonTitle: "YouTube Shorts URLを通常のYouTubeビデオURLに変換します",
            },
        },
        {
            name: "Chinese (Simplified)",
            match: ["zh-CN"],
            lang: {
                buttonText: "Shorts转换",
                buttonTitle: "将 YouTube Shorts 网址转换为常规的 YouTube 视频网址",
            },
        },
        {
            name: "Korean",
            match: ["ko"],
            lang: {
                buttonText: "Shorts변환",
                buttonTitle: "YouTube Shorts URL을 일반 YouTube 비디오 URL로 변환합니다",
            },
        },
        {
            name: "Russian",
            match: ["ru"],
            lang: {
                buttonText: "Shorts конвертация",
                buttonTitle: "Преобразование URL YouTube Shorts в обычный URL видео YouTube",
            },
        },
        {
            name: "German",
            match: ["de"],
            lang: {
                buttonText: "Shorts konvertieren",
                buttonTitle: "Konvertiere YouTube Shorts URL in reguläre YouTube Video URL",
            },
        },
        {
            name: "Spanish",
            match: ["es"],
            lang: {
                buttonText: "Convertir Shorts",
                buttonTitle: "Convierte la URL de YouTube Shorts en una URL de video de YouTube normal",
            },
        },
        {
            name: "French",
            match: ["fr"],
            lang: {
                buttonText: "Convertir Shorts",
                buttonTitle: "Convertir l'URL YouTube Shorts en URL vidéo YouTube classique",
            },
        },
        {
            name: "Italian",
            match: ["it"],
            lang: {
                buttonText: "Converti Shorts",
                buttonTitle: "Converti l'URL YouTube Shorts in URL video YouTube normale",
            },
        },
    ];

    function debugLog(message) {
        if (debug) {
            console.log("[DEBUG] " + message);
        }
    }

    function getLanguageData() {
        for (const data of langData) {
            if (data.match.includes(lang)) {
                debugLog(`檢測到的語言: ${data.name}`);
                return data.lang;
            }
        }
        debugLog(`找不到語言,預設為 ${langData[0].name}`);
        return langData[0].lang;
    }

    const languageData = getLanguageData();

    function createConvertButton() {
        if (!convertButton) {
            debugLog("正在創建按鈕...");
            convertButton = document.createElement('button');
            convertButton.textContent = languageData.buttonText;
            convertButton.style.position = 'fixed';
            convertButton.style.top = '150px';
            convertButton.style.right = '10px';
            convertButton.style.zIndex = '9999';
            convertButton.style.backgroundColor = '#FF0000';
            convertButton.style.color = '#FFFFFF';
            convertButton.style.fontSize = '24px';
            convertButton.style.padding = '10px 20px';
            convertButton.style.border = 'none';
            convertButton.style.borderRadius = '5px';
            convertButton.title = languageData.buttonTitle;
            document.body.appendChild(convertButton);

            convertButton.addEventListener('click', convertURL);
            convertButton.addEventListener('auxclick', function(event) {
                convertURL(event);
            });

            convertButton.addEventListener('mousedown', function() {
                convertButton.style.backgroundColor = '#D80000';
                isButtonDown = true;
            });

            convertButton.addEventListener('mouseup', function() {
                convertButton.style.backgroundColor = '#FF0000';
                isButtonDown = false;
            });

            convertButton.addEventListener('mouseout', function() {
                if (!isButtonDown) {
                    convertButton.style.backgroundColor = '#FF0000';
                }
            });

            convertButton.addEventListener('mouseenter', function() {
                convertButton.style.backgroundColor = '#FF3333';
            });

            convertButton.addEventListener('mouseleave', function() {
                convertButton.style.backgroundColor = '#FF0000';
            });

            debugLog("按鈕創建成功");
        }
    }

    function convertURL(event) {
        const currentURL = window.location.href;
        debugLog("當前網址: " + currentURL);

        if (currentURL.includes("youtube.com/shorts/")) {
            const match = currentURL.match(/https:\/\/www\.youtube\.com\/shorts\/([A-Za-z0-9_-]+)/);
            if (match) {
                const videoID = match[1];
                const videoURL = `https://www.youtube.com/watch?v=${videoID}`;
                debugLog(`匹配到的影片ID: ${videoID}`);
                debugLog(`轉換後的網址: ${videoURL}`);

                if (event && event.button === 2) {
                    debugLog("檢測到右鍵點擊(未執行任何操作)");
                } else if (event && event.button === 1) {
                    debugLog("檢測到中鍵點擊(開啟新標籤)");
                    window.open(videoURL, '_blank');
                } else {
                    debugLog("正在導航至轉換後的網址");
                    window.location.href = videoURL;
                }
            } else {
                debugLog("網址中沒有匹配到影片ID");
            }
        } else {
            debugLog("不是 YouTube Shorts 網址");
        }
    }

    function removeConvertButton() {
        if (convertButton) {
            debugLog("正在移除按鈕...");
            convertButton.remove();
            convertButton = null;
            debugLog("按鈕已移除");
        }
    }

    function debounce(func, wait) {
        let timeout;
        return function executedFunction(...args) {
            const later = () => {
                timeout = null;
                func(...args);
            };
            clearTimeout(timeout);
            timeout = setTimeout(later, wait);
        };
    }

    const checkAndCreateButton = debounce(function() {
        debugLog("檢查是否應該創建按鈕...");
        if (window.location.href.includes("youtube.com/shorts/")) {
            createConvertButton();
        } else {
            removeConvertButton();
        }
    }, 200); // 防抖200ms

    window.addEventListener('popstate', checkAndCreateButton);

    const observer = new MutationObserver(function(mutationsList) {
        for (const mutation of mutationsList) {
            if (mutation.type === 'childList') {
                checkAndCreateButton();
            }
        }
    });

    observer.observe(document.documentElement, { childList: true, subtree: true });

})();