您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
以新分頁或新視窗,同時播放複數Youtube影片。
// ==UserScript== // @name YouTube 多重播放器 // @namespace http://tampermonkey.net/ // @version 4.3 // @match https://www.youtube.com/ // @match https://www.youtube.com/feed/* // @match https://www.youtube.com/playlist?list=* // @match https://www.youtube.com/@* // @match https://www.youtube.com/gaming // @exclude https://www.youtube.com/watch* // @grant none // @license MIT // @description 以新分頁或新視窗,同時播放複數Youtube影片。 // ==/UserScript== (function(){ const validateURL = () => { const patterns = [ /^https:\/\/www\.youtube\.com\/$/, /^https:\/\/www\.youtube\.com\/feed\/.*/, /^https:\/\/www\.youtube\.com\/playlist\?list=.*/, /^https:\/\/www\.youtube\.com\/@.*/, /^https:\/\/www\.youtube\.com\/gaming$/ ]; return patterns.some(p => p.test(window.location.href)); }; setTimeout(() => { if(!validateURL()){ const panel = document.getElementById('ytMulti_panel'); if(panel) panel.remove(); return; } }, 60000); const STORAGE_POS = 'ytMulti_btnPos'; const STORAGE_LIST1 = 'ytMulti_videoList1'; const STORAGE_LIST2 = 'ytMulti_videoList2'; const STORAGE_MODE = 'ytMulti_openMode'; const STORAGE_CURRENT = 'ytMulti_currentList'; let currentList = localStorage.getItem(STORAGE_CURRENT) || 'list1'; const panel = document.createElement('div'); panel.id = 'ytMulti_panel'; panel.style.cssText = ` position: fixed; background: rgba(0,0,0,0.8); color: #fff; padding: 8px 12px; border-radius: 8px; z-index: 9999; display: flex; align-items: center; cursor: move; gap: 10px; box-shadow: 0 4px 12px rgba(0,0,0,0.2); font-family: Arial, sans-serif; backdrop-filter: blur(4px); `; document.body.appendChild(panel); const savedPos = JSON.parse(localStorage.getItem(STORAGE_POS) || 'null'); if(savedPos){ panel.style.top = savedPos.top; panel.style.left = savedPos.left; panel.style.right = 'auto'; } panel.addEventListener('mousedown', e => { e.preventDefault(); let startX = e.clientX, startY = e.clientY; const rect = panel.getBoundingClientRect(); function onMove(ev){ panel.style.top = rect.top + ev.clientY - startY + 'px'; panel.style.left = rect.left + ev.clientX - startX + 'px'; } function onUp(){ localStorage.setItem(STORAGE_POS, JSON.stringify({top: panel.style.top, left: panel.style.left})); window.removeEventListener('mousemove', onMove); window.removeEventListener('mouseup', onUp); } window.addEventListener('mousemove', onMove); window.addEventListener('mouseup', onUp); }); function createStyledButton(text){ const btn = document.createElement('button'); btn.textContent = text; btn.style.cssText = ` padding: 8px 16px; border: none; border-radius: 6px; background: #ff0000; color: white; cursor: pointer; transition: all 0.2s; font-size: 14px; font-weight: 500; text-shadow: 0 1px 2px rgba(0,0,0,0.2); box-shadow: 0 2px 4px rgba(0,0,0,0.2); `; btn.addEventListener('mouseover', () => btn.style.background = '#cc0000'); btn.addEventListener('mouseout', () => btn.style.background = '#ff0000'); return btn; } const playBtn = createStyledButton('▶ 播放'); const modeBtn = createStyledButton(localStorage.getItem(STORAGE_MODE) === 'tab' ? '分頁' : '視窗'); const listBtn = createStyledButton(currentList === 'list1' ? 'List1' : 'List2'); panel.append(playBtn, modeBtn, listBtn); panel.addEventListener('dragover', e => e.preventDefault()); panel.addEventListener('drop', e => { e.preventDefault(); const data = e.dataTransfer.getData('text/uri-list') || e.dataTransfer.getData('text/plain'); const vid = parseYouTubeID(data); if(!vid) return; const storageKey = currentList === 'list1' ? STORAGE_LIST1 : STORAGE_LIST2; const ids = JSON.parse(localStorage.getItem(storageKey) || '[]'); if(!ids.includes(vid)){ ids.push(vid); localStorage.setItem(storageKey, JSON.stringify(ids)); listBtn.textContent = currentList === 'list1' ? `List1 (${ids.length})` : `List2 (${ids.length})`; } }); modeBtn.addEventListener('click', () => { const mode = localStorage.getItem(STORAGE_MODE) === 'tab' ? 'window' : 'tab'; localStorage.setItem(STORAGE_MODE, mode); modeBtn.textContent = mode === 'tab' ? '分頁' : '視窗'; }); listBtn.addEventListener('click', () => { currentList = currentList === 'list1' ? 'list2' : 'list1'; localStorage.setItem(STORAGE_CURRENT, currentList); const storageKey = currentList === 'list1' ? STORAGE_LIST1 : STORAGE_LIST2; const count = JSON.parse(localStorage.getItem(storageKey) || '[]').length; listBtn.textContent = currentList === 'list1' ? `List1 (${count})` : `List2 (${count})`; }); playBtn.addEventListener('click', () => { const storageKey = currentList === 'list1' ? STORAGE_LIST1 : STORAGE_LIST2; const ids = JSON.parse(localStorage.getItem(storageKey) || '[]'); if(!ids.length) return alert('當前清單無影片'); const html = makeBlobPage(ids, currentList); const blobUrl = URL.createObjectURL(new Blob([html], {type: 'text/html'})); const mode = localStorage.getItem(STORAGE_MODE); mode === 'tab' ? window.open(blobUrl, '_blank') : window.open(blobUrl, '_blank', 'width=800,height=600'); }); function parseYouTubeID(url){ const m = url.match(/(?:v=|youtu\.be\/)([A-Za-z0-9_-]{11})/); return m ? m[1] : null; } function makeBlobPage(ids, listKey){ const listJson = JSON.stringify(ids); return `<!DOCTYPE html><html><head><meta charset="UTF-8"><title>多重播放</title><style> body{margin:0;padding:0;background:#000;overflow:hidden;} .container{position:absolute;top:0;left:0;width:100vw;height:100vh;display:flex;flex-wrap:wrap;align-content:flex-start;} .video-wrapper{position:relative;overflow:hidden;will-change:transform;} .video-wrapper iframe{width:100%;height:100%;border:none;transform:scale(0.999);} .remove-btn{ position:absolute;top:6px;right:6px; width:20px;height:20px; background:#ff4444; border-radius:3px; display:none; cursor:pointer; z-index:9999; box-shadow:0 0 3px rgba(0,0,0,0.3); } .remove-btn::after{ content:'×'; color:white; font-size:16px; position:absolute; top:50%;left:50%; transform:translate(-50%,-50%); } .video-wrapper:hover .remove-btn{display:block;} </style></head><body><div class="container"></div><script> const ASPECT_RATIO = 16/9; const ids = ${listJson}; const listKey = ${JSON.stringify(listKey)}; const container = document.querySelector('.container'); function calculateLayout(){ const W = container.offsetWidth; const H = container.offsetHeight; const n = ids.length; if(n === 0) return {cols:0, rows:0, itemWidth:0, itemHeight:0}; let bestCols = 1; let bestRows = 1; let bestItemWidth = 0; let bestItemHeight = 0; let bestScore = 0; for(let cols=1; cols<=Math.min(n,12); cols++){ const rows = Math.ceil(n/cols); let itemWidth = W/cols; let itemHeight = itemWidth/ASPECT_RATIO; if(rows*itemHeight > H){ itemHeight = H/rows; itemWidth = itemHeight*ASPECT_RATIO; } const usedWidth = cols*itemWidth; const usedHeight = rows*itemHeight; const areaScore = usedWidth*usedHeight; const penalty = (W-usedWidth)*0.1 + (H-usedHeight)*0.2; const totalScore = areaScore - penalty; if(totalScore > bestScore){ bestScore = totalScore; bestCols = cols; bestRows = rows; bestItemWidth = itemWidth; bestItemHeight = itemHeight; } } return {cols:bestCols, rows:bestRows, itemWidth:bestItemWidth, itemHeight:bestItemHeight}; } function updateLayout(){ const {cols, rows, itemWidth, itemHeight} = calculateLayout(); container.style.fontSize = '0'; Array.from(container.children).forEach((wrap, index) => { const col = index%cols; const row = Math.floor(index/cols); wrap.style.width = itemWidth + 'px'; wrap.style.height = itemHeight + 'px'; wrap.style.transform = \`translate(\${col*itemWidth}px, \${row*itemHeight}px)\`; }); } function createVideo(id, idx){ const wrap = document.createElement('div'); wrap.className = 'video-wrapper'; wrap.style.display = 'inline-block'; wrap.style.position = 'absolute'; const ifr = document.createElement('iframe'); ifr.src = 'https://www.youtube.com/embed/'+id+'?autoplay=1&playsinline=1&rel=0&modestbranding=1&origin='+encodeURIComponent(window.location.origin); ifr.allow = 'autoplay; encrypted-media; fullscreen'; ifr.allowfullscreen = true; const delBtn = document.createElement('div'); delBtn.className = 'remove-btn'; delBtn.onclick = () => { const storageKey = listKey === 'list1' ? 'ytMulti_videoList1' : 'ytMulti_videoList2'; const stored = JSON.parse(localStorage.getItem(storageKey) || '[]'); stored.splice(stored.indexOf(id), 1); localStorage.setItem(storageKey, JSON.stringify(stored)); wrap.remove(); updateLayout(); }; wrap.append(ifr, delBtn); return wrap; } ids.forEach((id, idx) => container.appendChild(createVideo(id, idx))); updateLayout(); window.addEventListener('resize', updateLayout); setInterval(updateLayout, 500); <\/script></body></html>`; } const initListCount = () => { const count1 = JSON.parse(localStorage.getItem(STORAGE_LIST1) || '[]').length; const count2 = JSON.parse(localStorage.getItem(STORAGE_LIST2) || '[]').length; listBtn.textContent = currentList === 'list1' ? `List1 (${count1})` : `List2 (${count2})`; }; initListCount(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址