您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Enhance BigBlueButton recording playback with extra controls
// ==UserScript== // @name BBB Playback Controls+ (FINKI) // @namespace sj14apps.bbb.playerplus // @version 1.1.1 // @description Enhance BigBlueButton recording playback with extra controls // @author SlaVcE14 // @license MIT // @match https://bbb-lb.finki.ukim.mk/playback/presentation/* // @run-at document-idle // @grant none // ==/UserScript== (function () { "use strict"; const SPEED_KEY = "bbb_playerplus_speed"; // Utility: get & set preferred speed const getSavedSpeed = () => { try { return parseFloat(localStorage.getItem(SPEED_KEY)) || 1.0; } catch { return 1.0; } }; const saveSpeed = (v) => { try { localStorage.setItem(SPEED_KEY, String(v)); } catch {} }; const seen = new WeakSet(); function upgradeVideo(video) { if (!video || seen.has(video)) return; seen.add(video); // Start with saved speed const initialSpeed = getSavedSpeed(); if (!Number.isNaN(initialSpeed) && initialSpeed > 0) video.playbackRate = initialSpeed; // Helpers const setSpeed = (val) => { const v = Math.max(0.25, Math.min(3.0, val)); video.playbackRate = v; saveSpeed(v); }; // Keyboard shortcuts document.addEventListener("keydown", (ev) => { // Ignore if typing in inputs const t = ev.target; if (t && (t.tagName === "INPUT" || t.tagName === "TEXTAREA" || t.isContentEditable)) return; switch (ev.key.toLowerCase()) { case "k": case " ": ev.preventDefault(); video.paused ? video.play().catch(()=>{}) : video.pause(); break; case "j": case "arrowleft": ev.preventDefault(); video.currentTime = Math.max(0, video.currentTime - 10); break; case "l": case "arrowright": ev.preventDefault(); video.currentTime = Math.min(video.duration || Infinity, video.currentTime + 10); break; case "[": ev.preventDefault(); setSpeed(video.playbackRate - 0.25); break; case "]": ev.preventDefault(); setSpeed(video.playbackRate + 0.25); break; case "m": ev.preventDefault(); video.muted = !video.muted; break; } }, { passive: false }); } // Find videos in main document and same-origin iframes function scan(root) { root.querySelectorAll("video").forEach(upgradeVideo); root.querySelectorAll("iframe").forEach((ifr) => { try { if (ifr.contentDocument) scan(ifr.contentDocument); } catch {} }); } // Initial + observe changes scan(document); const mo = new MutationObserver(() => scan(document)); mo.observe(document.documentElement, { childList: true, subtree: true }); })();