您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Plays audio associated with images on 4chan.
当前为
// ==UserScript== // @name 4chan External Sounds // @namespace b4k // @description Plays audio associated with images on 4chan. // @author Bakugo // @version 1.0.0 // @match *://boards.4chan.org/* // @run-at document-start // ==/UserScript== (function() { var regexFileURL; var regexTrigger; var audioPlayers; var processFile; var processFiles; var getFileKey; var playFile; regexFileURL = /\/\/i.4cdn.org\/(.+?)\/(\d+?)\.(.+?)$/; regexTrigger = /\[audio[\|\$](.*?)\]/i; audioPlayers = {}; document.addEventListener("4chanXInitFinished", function (event) { if (!document.documentElement.classList.contains("fourchan-x")) { return; } processFiles(document.body); (new MutationObserver(function (mutations) { mutations.forEach(function (mutation) { if (mutation.type === "childList") { mutation.addedNodes.forEach(function (node) { if (node.nodeType === Node.ELEMENT_NODE) { processFiles(node); playFile(node); } }); } }); })).observe(document.body, { childList: true, subtree: true }); }); processFile = function (file) { var fileLink; var fileKey; var fileName; var fileNameEls; var fileNameMatch; var audioURL; var audioPlayer; if (!file.classList.contains("file")) { return; } fileLink = file.querySelector(".file-info > a"); if (!fileLink) { return; } fileKey = getFileKey(fileLink.href); if (!fileKey) { return; } fileName = null; fileNameEls = [ file.querySelector(".file-info .fnfull"), file.querySelector(".file-info > a") ]; fileNameEls.forEach(function (node) { if (fileName) { return; } if (node && node.textContent) { fileName = node.textContent; } }); if (!fileName) { return; } fileNameMatch = fileName.match(regexTrigger); if (!fileNameMatch) { return; } audioURL = fileNameMatch[1]; audioURL = window.decodeURIComponent(audioURL); audioURL = (audioURL.match(/^(https?\:)?\/\//) ? audioURL : ("http://" + audioURL)); audioPlayer = document.createElement("audio"); audioPlayer.src = audioURL; audioPlayer.preload = "none"; audioPlayers[fileKey] = audioPlayer; }; processFiles = function (target) { target.querySelectorAll(".post") .forEach(function (post) { if (post.parentElement.parentElement.id === "qp") { return; } if (post.parentElement.classList.contains("noFile")) { return; } post.querySelectorAll(".file") .forEach(function (file) { processFile(file); }); }); }; getFileKey = function (fileURL) { var fileURLMatch; fileURLMatch = fileURL.match(regexFileURL); if (!fileURLMatch) { return null; } return (fileURLMatch[1] + "." + fileURLMatch[2]); }; playFile = function (target) { var fileKey; var audioPlayer; var checkInterval; if (!(target.id === "ihover" || target.className === "full-image")) { return; } fileKey = getFileKey(target.src); if (!fileKey) { return; } audioPlayer = audioPlayers[fileKey]; if (!audioPlayer) { return; } if (!audioPlayer.paused) { return; } switch (target.tagName) { case "IMG": audioPlayer.currentTime = 0; audioPlayer.loop = true; audioPlayer.play(); break; case "VIDEO": audioPlayer.currentTime = target.currentTime; audioPlayer.loop = false; audioPlayer.play(); break; default: return; } checkInterval = window.setInterval(function () { if (document.body.contains(target)) { if (target.tagName === "VIDEO") { if (audioPlayer.duration) { if (target.currentTime > audioPlayer.duration) { audioPlayer.pause(); } else { if (Math.abs(target.currentTime - audioPlayer.currentTime) > 0.1) { audioPlayer.currentTime = target.currentTime; } if (!target.paused && audioPlayer.paused) { audioPlayer.play(); } if (target.paused && !audioPlayer.paused) { audioPlayer.pause(); } } } } } else { window.clearInterval(checkInterval); audioPlayer.pause(); } }, 10); }; })();