// ==UserScript== // @name 2chan f*.mp4 Extractor & Player (動的追加対応) // @namespace http://tampermonkey.net/ // @version 1.6 // @description f*.mp4 を抽出し順番再生。新規レスにも対応 // @match *://img.2chan.net/b/res/* // @run-at document-end // @grant none // ==/UserScript== (function() { 'use strict'; console.log("[MP4Player] スクリプト開始:", location.href); const re = /\bf[a-z0-9]+\.mp4\b/gi; const urls = []; const urlSet = new Set(); let total = 0; // === コンテナ & プレイヤー (前のバージョンと同じ) === const container = document.createElement("div"); container.style.position = "fixed"; container.style.bottom = "10px"; container.style.right = "10px"; container.style.zIndex = 999999; container.style.background = "rgba(0,0,0,0.6)"; container.style.padding = "6px"; container.style.border = "2px solid red"; container.style.borderRadius = "6px"; container.style.display = "flex"; container.style.flexDirection = "column"; container.style.alignItems = "center"; document.body.appendChild(container); const video = document.createElement("video"); video.controls = true; video.autoplay = true; video.style.backgroundColor = "black"; container.appendChild(video); const controls = document.createElement("div"); controls.style.display = "flex"; controls.style.alignItems = "center"; controls.style.gap = "6px"; controls.style.marginTop = "4px"; container.appendChild(controls); const info = document.createElement("span"); info.style.color = "white"; controls.appendChild(info); const prevBtn = document.createElement("button"); prevBtn.textContent = "◀ 前"; controls.appendChild(prevBtn); const nextBtn = document.createElement("button"); nextBtn.textContent = "次 ▶"; controls.appendChild(nextBtn); const jumpInput = document.createElement("input"); jumpInput.type = "number"; jumpInput.style.width = "50px"; controls.appendChild(jumpInput); const jumpBtn = document.createElement("button"); jumpBtn.textContent = "ジャンプ"; controls.appendChild(jumpBtn); const sizeBtn = document.createElement("button"); sizeBtn.textContent = "サイズ切替"; controls.appendChild(sizeBtn); let index = 0; let large = false; function updateInfo() { info.textContent = `動画 ${index + 1} / ${total}`; jumpInput.min = 1; jumpInput.max = total; jumpInput.value = index + 1; } function adjustVideoSize() { const vw = video.videoWidth; const vh = video.videoHeight; if (!vw || !vh) return; const base = large ? 960 : 480; if (vw >= vh) { video.width = base; video.height = Math.round(base * (vh / vw)); } else { video.height = base; video.width = Math.round(base * (vw / vh)); } } function playAt(i) { if (i < 0 || i >= total) return; index = i; const url = urls[index]; console.log("[MP4Player] 再生開始:", url); video.src = url; video.play().catch(err => console.error("[MP4Player] 再生エラー:", err)); updateInfo(); } function playNext() { if (index + 1 < total) { setTimeout(() => playAt(index + 1), 2000); } else { console.log("[MP4Player] 全動画再生終了"); } } function playPrev() { if (index - 1 >= 0) playAt(index - 1); } video.addEventListener("ended", playNext); video.addEventListener("loadedmetadata", adjustVideoSize); prevBtn.addEventListener("click", playPrev); nextBtn.addEventListener("click", () => playAt(index + 1)); jumpBtn.addEventListener("click", () => playAt(parseInt(jumpInput.value, 10) - 1)); sizeBtn.addEventListener("click", () => { large = !large; adjustVideoSize(); }); // === 初期走査 & 動的監視 === function addFromText(text) { const matches = text.match(re); if (!matches) return; matches.forEach(name => { const url = /^fu/i.test(name) ? "https://dec.2chan.net/up2/src/" + name : "https://dec.2chan.net/up/src/" + name; if (!urlSet.has(url)) { urlSet.add(url); urls.push(url); total = urls.length; console.log("[MP4Player] 新規追加:", url); } }); updateInfo(); } // 初期本文 addFromText(document.body.innerText); // DOM変化を監視 const observer = new MutationObserver(mutations => { for (const m of mutations) { for (const node of m.addedNodes) { if (node.nodeType === Node.TEXT_NODE) { addFromText(node.textContent); } else if (node.nodeType === Node.ELEMENT_NODE) { addFromText(node.innerText || ""); } } } }); observer.observe(document.body, { childList: true, subtree: true }); // 再生開始 if (urls.length > 0) playAt(0); })();