您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
停用重播 解除靜音 可點擊進度條 按 H 顯示/隱藏面板
// ==UserScript== // @name facebook.com 停用重播 解除靜音 可點擊進度條 按 H 顯示/隱藏面板 // @namespace fb Scripts // @match *://www.facebook.com/* // @grant none // @icon https://www.google.com/s2/favicons?sz=64&domain=facebook.com // @version 1.1 // @author huang-wei-lun // @license MIT // @description 停用重播 解除靜音 可點擊進度條 按 H 顯示/隱藏面板 // ==/UserScript== (function () { // 狀態 let disableLoop = JSON.parse(localStorage.getItem("fb_disableLoop") || "true"); let unmuteVideo = JSON.parse(localStorage.getItem("fb_unmuteVideo") || "true"); let clickableBar = JSON.parse(localStorage.getItem("fb_clickableBar") || "true"); // 建立控制面板 const panel = document.createElement("div"); Object.assign(panel.style, { position: "fixed", top: "20px", right: "20px", zIndex: 999999, background: "rgba(32,32,32,0.92)", color: "#e4e4e4", padding: "10px", borderRadius: "10px", fontFamily: "system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif", fontSize: "14px", border: "1px solid #3c3c3c", boxShadow: "0 6px 16px rgba(0,0,0,0.4)", transition: "opacity .25s" }); panel.innerHTML = ` <label style="display:block;margin-bottom:6px;"> <input type="checkbox" id="fb_disableLoop"> 停用重播 </label> <label style="display:block;margin-bottom:6px;"> <input type="checkbox" id="fb_unmuteVideo"> 解除靜音 </label> <label style="display:block;margin-bottom:6px;"> <input type="checkbox" id="fb_clickableBar"> 可點擊進度條 </label> <hr style="border:0;border-top:1px solid #3c3c3c;margin:8px 0;"> <small style="color:#a8a8a8;">按 H 顯示/隱藏面板</small> `; document.body.appendChild(panel); panel.querySelector("#fb_disableLoop").checked = disableLoop; panel.querySelector("#fb_unmuteVideo").checked = unmuteVideo; panel.querySelector("#fb_clickableBar").checked = clickableBar; panel.querySelector("#fb_disableLoop").addEventListener("change", (e) => { disableLoop = e.target.checked; localStorage.setItem("fb_disableLoop", JSON.stringify(disableLoop)); applyToAllVideos(); }); panel.querySelector("#fb_unmuteVideo").addEventListener("change", (e) => { unmuteVideo = e.target.checked; localStorage.setItem("fb_unmuteVideo", JSON.stringify(unmuteVideo)); applyToAllVideos(); }); panel.querySelector("#fb_clickableBar").addEventListener("change", (e) => { clickableBar = e.target.checked; localStorage.setItem("fb_clickableBar", JSON.stringify(clickableBar)); applyToAllVideos(); }); // 面板快捷鍵 let visible = true; document.addEventListener("keydown", (e) => { if (e.key.toLowerCase() === "h") { visible = !visible; panel.style.opacity = visible ? "1" : "0"; panel.style.pointerEvents = visible ? "auto" : "none"; } }); // 進度條樣式(覆蓋在影片上) function ensureClickableBar(video) { if (!clickableBar || video.__fbHasBar) return; const container = video.parentElement; if (!container) return; const bar = document.createElement("div"); Object.assign(bar.style, { position: "absolute", left: "0", right: "0", bottom: "6px", height: "6px", background: "rgba(255,255,255,0.28)", cursor: "pointer", zIndex: 99998, borderRadius: "999px", overflow: "hidden", backdropFilter: "blur(1px)" }); const prog = document.createElement("div"); Object.assign(prog.style, { height: "100%", width: "0%", background: "rgba(255,255,255,0.9)" }); bar.appendChild(prog); // 點擊跳轉 bar.addEventListener("click", (e) => { const rect = bar.getBoundingClientRect(); const percent = Math.min(1, Math.max(0, (e.clientX - rect.left) / rect.width)); if (isFinite(video.duration) && video.duration > 0) { video.currentTime = video.duration * percent; } }); // 跟隨時間更新 const onTime = () => { if (!isFinite(video.duration) || video.duration <= 0) return; const p = (video.currentTime / video.duration) * 100; prog.style.width = p + "%"; }; video.addEventListener("timeupdate", onTime); video.addEventListener("progress", onTime); video.addEventListener("loadedmetadata", onTime); onTime(); // 確保父容器能定位 const prevPos = container.style.position; if (getComputedStyle(container).position === "static") { container.style.position = "relative"; } container.appendChild(bar); // 標記並保存清理器 video.__fbHasBar = true; video.__fbBar = bar; video.__fbBarCleanup = () => { video.removeEventListener("timeupdate", onTime); video.removeEventListener("progress", onTime); video.removeEventListener("loadedmetadata", onTime); if (bar.isConnected) bar.remove(); if (prevPos) container.style.position = prevPos; video.__fbHasBar = false; video.__fbBar = null; video.__fbBarCleanup = null; }; } function removeClickableBar(video) { if (video.__fbBarCleanup) video.__fbBarCleanup(); } // 對單一 video 套用規則 function applyRules(video) { try { if (disableLoop) video.loop = false; if (unmuteVideo) { video.muted = false; video.defaultMuted = false; // 強化一次,避免 FB 程式碼又設回去 video.volume = 1.0; // 嘗試開啟音軌(有些瀏覽器需互動才允許播放聲音) const resume = () => { video.muted = false; video.defaultMuted = false; video.volume = 1.0; }; video.addEventListener("play", resume, { once: true }); } if (clickableBar) { ensureClickableBar(video); } else { removeClickableBar(video); } } catch (_) {} } // 初次套用 function applyToAllVideos() { document.querySelectorAll("video").forEach((v) => applyRules(v)); } applyToAllVideos(); // 監聽動態載入(Facebook 是 SPA) const obs = new MutationObserver((muts) => { for (const mut of muts) { mut.addedNodes.forEach((node) => { if (node instanceof HTMLVideoElement) { applyRules(node); } else if (node.querySelectorAll) { node.querySelectorAll("video").forEach((v) => applyRules(v)); } }); // 若節點被移除,清理進度條 mut.removedNodes.forEach((node) => { if (node instanceof HTMLVideoElement) { removeClickableBar(node); } else if (node.querySelectorAll) { node.querySelectorAll("video").forEach(removeClickableBar); } }); } }); obs.observe(document.documentElement, { childList: true, subtree: true }); // 小提示:滾動時有新影片載入也會自動套用 console.log("[FB Video Helper] 已啟用:停用重播 =", disableLoop, "解除靜音 =", unmuteVideo, "可點擊進度條 =", clickableBar); })();