您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Original panel/logic; fixed autoplay & random for wcostream.tv; resilient to ads
// ==UserScript== // @name WCO Auto-Play Next/Random with Dice (v1.4 — wcostream fixed) // @namespace http://tampermonkey.net/ // @author P3k // @version 1.4 // @license GNU GENERAL PUBLIC LICENSE // @description Original panel/logic; fixed autoplay & random for wcostream.tv; resilient to ads // @match *://wcostream.tv/* // @match *://www.wcostream.tv/* // @match *://embed.wcostream.com/* // @grant none // @run-at document-start // ==/UserScript== (function () { 'use strict'; // ----------------------------------------------------------------------------- // 1) STUBS (unchanged) // ----------------------------------------------------------------------------- window.downloadJSAtOnload = () => {}; window.sub = () => {}; // ----------------------------------------------------------------------------- // 2) IFRAME CONTEXT (now ALWAYS hooks "ended"; autoplay click only if requested) // ----------------------------------------------------------------------------- if (window.top !== window.self) { // Always hook ended so parent can decide next/random regardless of autoplay click const endedObs = new MutationObserver((_, obs) => { const v = document.querySelector('video'); if (v) { try { v.muted = false; } catch {} v.addEventListener('ended', () => { window.parent.postMessage({ type: 'WCO_VIDEO_ENDED' }, '*'); }); obs.disconnect(); } }); endedObs.observe(document.documentElement, { childList: true, subtree: true }); // Only auto-click big play when parent tells us autoplay is enabled window.addEventListener('message', (event) => { if (event.data?.type === 'WCO_AUTOPLAY_PREF' && event.data.autoplay) { const playObs = new MutationObserver((_, obs) => { const btn = document.querySelector('button.vjs-big-play-button'); if (btn) { btn.click(); obs.disconnect(); } }); playObs.observe(document.documentElement, { childList: true, subtree: true }); } }); return; } // ----------------------------------------------------------------------------- // 3) PARENT PAGE CONTEXT // ----------------------------------------------------------------------------- const episodes = []; // random + fallback next use this function abs(href, base) { try { return new URL(href, base || location.origin).href; } catch { return null; } } function samePath(a, b) { try { const A = new URL(a, location.origin); const B = new URL(b, location.origin); return A.pathname.replace(/\/+$/,'') === B.pathname.replace(/\/+$/,''); } catch { return a === b; } } function pushEpisode(url, title) { if (!url) return; if (!episodes.some(e => samePath(e.url, url))) episodes.push({ url, title: title || url }); } // Collect from THIS page immediately (sidebar + main list if present) function collectFromCurrentPage(doc = document, base) { // Main list (if on the show page like the HTML you pasted) doc.querySelectorAll('#catlist-listview li a, #catlist-listview a.sonra').forEach(a => { pushEpisode(abs(a.getAttribute('href'), base), a.textContent.trim()); }); // Sidebar mirror (present on episode pages) doc.querySelectorAll('#sidebar .menustyle a, #sidebar .menusttyle a').forEach(a => { pushEpisode(abs(a.getAttribute('href'), base), a.textContent.trim()); }); } // Also try fetching the canonical show page to be thorough function deriveSlugFromLocation() { const seg = location.pathname.replace(/\/+$/,'').split('/')[1] || ''; const m = seg.match(/^(.+?)-(?:season|episode|special|ova)\b/i); return (m && m[1]) ? m[1] : seg || null; } function buildShowCandidates() { const out = new Set(); document.querySelectorAll('a[href^="/playlist-cat/"]').forEach(a => { const slug = (a.getAttribute('href') || '').split('/').pop(); if (slug) { out.add(abs('/' + slug)); out.add(abs('/anime/' + slug)); } }); const slug = deriveSlugFromLocation(); if (slug) { out.add(abs('/' + slug)); out.add(abs('/anime/' + slug)); } return Array.from(out); } function parseShowHTML(html, base) { const doc = new DOMParser().parseFromString(html, 'text/html'); collectFromCurrentPage(doc, base); } async function loadShowEpisodes() { const candidates = buildShowCandidates(); for (const url of candidates) { try { const res = await fetch(url, { credentials: 'include' }); if (!res.ok) continue; const html = await res.text(); if (!/#catlist-listview/.test(html)) continue; // must look like show page parseShowHTML(html, url); if (episodes.length) return true; } catch {} } return false; } // ----------------------------------------------------------------------------- // 4) ORIGINAL PANEL (exact layout; layered above ads) // ----------------------------------------------------------------------------- function makePanel(iframe) { const storedNext = localStorage.getItem('wco-auto-next'); const storedRand = localStorage.getItem('wco-auto-random'); const storedAuto = localStorage.getItem('wco-auto-play'); const defaultNext = (storedNext === null && storedRand === null) ? true : (storedNext === 'true'); const defaultRand = (storedRand === 'true'); const defaultAuto = (storedAuto === null) ? true : (storedAuto === 'true'); const outer = document.createElement('div'); outer.style.cssText = ` display:flex;flex-direction:row;justify-content:center;gap:16px;margin:12px auto; font-family:sans-serif;font-size:14px;background:transparent; position:relative;z-index:2147483647;pointer-events:auto; `; function makeToggle(id, labelText, isChecked) { const label = document.createElement('label'); label.style.cssText = 'display:flex;align-items:center;cursor:pointer;white-space:nowrap;'; const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.id = id; checkbox.checked = isChecked; checkbox.style.marginRight = '6px'; label.append(checkbox, document.createTextNode(labelText)); return { label, checkbox }; } // Section 1: Next Episode const section1 = document.createElement('div'); section1.style.cssText = 'border:1px solid #aaa;padding:8px;background:#fff;min-width:220px;'; const title1 = document.createElement('div'); title1.textContent = 'Next Episode'; title1.style.cssText = 'font-weight:bold;margin-bottom:6px;text-align:center;'; const toggles = document.createElement('div'); toggles.style.cssText = 'display:flex;flex-direction:row;gap:16px;justify-content:center;'; const nextToggle = makeToggle('wco-auto-next', 'Sequential', defaultNext); const randToggle = makeToggle('wco-auto-random', 'Random', defaultRand); nextToggle.checkbox.addEventListener('change', () => { if (nextToggle.checkbox.checked) randToggle.checkbox.checked = false; savePrefs(); }); randToggle.checkbox.addEventListener('change', () => { if (randToggle.checkbox.checked) nextToggle.checkbox.checked = false; savePrefs(); }); toggles.append(nextToggle.label, randToggle.label); section1.append(title1, toggles); // Dice const diceButton = document.createElement('button'); diceButton.innerHTML = '🎲'; diceButton.style.cssText = ` font-size:32px;width:50px;height:50px;border:1px solid #aaa;background:#fff; cursor:pointer;border-radius:8px;display:flex;align-items:center;justify-content:center;transition:transform .2s; `; diceButton.title = 'Play Random Episode'; diceButton.addEventListener('mouseenter', () => { diceButton.style.transform = 'scale(1.1)'; }); diceButton.addEventListener('mouseleave', () => { diceButton.style.transform = 'scale(1)'; }); diceButton.addEventListener('click', () => { if (episodes.length) { const pick = episodes[Math.floor(Math.random() * episodes.length)]; if (pick?.url) location.href = pick.url; } else { console.log('WCO: Episode list not loaded yet'); } }); // Section 2: Auto Play const section2 = document.createElement('div'); section2.style.cssText = 'border:1px solid #aaa;padding:8px;background:#fff;min-width:140px;'; const title2 = document.createElement('div'); title2.textContent = 'Auto Play'; title2.style.cssText = 'font-weight:bold;margin-bottom:6px;text-align:center;'; const box2 = document.createElement('div'); box2.style.cssText = 'display:flex;justify-content:center;'; const autoToggle = makeToggle('wco-auto-play', 'Enabled', defaultAuto); autoToggle.checkbox.addEventListener('change', () => { savePrefs(); postAutoplay(); // update iframe immediately }); box2.appendChild(autoToggle.label); section2.append(title2, box2); // Assemble in original order: AutoPlay | 🎲 | Next outer.append(section2, diceButton, section1); iframe.parentNode.insertBefore(outer, iframe.nextSibling); // Persist function savePrefs() { localStorage.setItem('wco-auto-next', nextToggle.checkbox.checked); localStorage.setItem('wco-auto-random', randToggle.checkbox.checked); localStorage.setItem('wco-auto-play', autoToggle.checkbox.checked); } // Push autoplay state to iframe (immediately + a few retries in case it was already loaded) function postAutoplay() { try { iframe.contentWindow.postMessage({ type: 'WCO_AUTOPLAY_PREF', autoplay: autoToggle.checkbox.checked }, '*'); } catch {} } // Send now, on load, and ping a few times to guarantee the embed sees it postAutoplay(); iframe.addEventListener('load', postAutoplay); const ping = setInterval(postAutoplay, 1500); setTimeout(() => clearInterval(ping), 8000); // stop after a few tries savePrefs(); } // ----------------------------------------------------------------------------- // 5) Handle video ended -> next/random (same behavior; better matching) // ----------------------------------------------------------------------------- window.addEventListener('message', (event) => { if (event.data?.type !== 'WCO_VIDEO_ENDED') return; const rand = localStorage.getItem('wco-auto-random') === 'true'; const next = (localStorage.getItem('wco-auto-next') === 'true') || (localStorage.getItem('wco-auto-next') === null && !rand); // default Next on first run if (rand && episodes.length) { const pick = episodes[Math.floor(Math.random() * episodes.length)]; if (pick?.url) location.href = pick.url; return; } if (next) { const rel = document.querySelector('a[rel="next"]'); if (rel?.href) { location.href = rel.href; return; } // Fallback: use our list (order on site is newest->oldest; "next" goes forward in list) if (episodes.length) { const idx = episodes.findIndex(e => samePath(e.url, location.href)); if (idx >= 0 && idx + 1 < episodes.length) { location.href = episodes[idx + 1].url; } } } }); // ----------------------------------------------------------------------------- // 6) Install panel when the player iframe shows up (ads-safe), and build episode list // ----------------------------------------------------------------------------- function isPlayerIframe(node) { if (!(node instanceof HTMLIFrameElement)) return false; const src = (node.getAttribute('src') || '').toLowerCase(); return src.includes('embed.wcostream.com') || src.includes('/inc/embed/video-js.php'); } function findPlayerIframe() { return Array.from(document.querySelectorAll('iframe')).find(isPlayerIframe) || null; } function installWhenIframeAppears() { const fr = findPlayerIframe(); if (fr) { makePanel(fr); return; } const mo = new MutationObserver(() => { const i = findPlayerIframe(); if (i) { makePanel(i); mo.disconnect(); } }); mo.observe(document.documentElement, { childList: true, subtree: true }); } // Build episodes immediately from this page, then try fetching the show page too if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { collectFromCurrentPage(); // sidebar list available on episode pages loadShowEpisodes(); // enhances the list if needed }); } else { collectFromCurrentPage(); loadShowEpisodes(); } if (document.readyState === 'complete') installWhenIframeAppears(); else window.addEventListener('load', installWhenIframeAppears); })();