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