实用脚本推广笔记|网页视频自由调速神器
日常上网课、刷课程、追剧看纪录片,总会遇到平台自带倍速档位太少、调节不够灵活的问题?固定几档速度没法贴合自己观看节奏,这款网页视频自定义调速油猴脚本,轻松解决变速难题!
一、脚本核心亮点
-
一键快捷调速 自带加减速度按键,点击
+/-就能快速增减播放倍速,还支持一键2倍速、一键恢复原速,操作简单顺手。 -
调速幅度随心改 默认每次调节幅度为0.5倍,可自行编辑脚本修改参数。想要精细化慢放调0.25倍步进,追求快速跳转改成1倍步进,按需适配观看习惯。
-
速度区间可控 可自主设置最高、最低播放速度上限下限,避免速度过快过慢导致视频卡顿、音画不同步,播放稳定性拉满。
-
多场景通用兼容 适配绝大多数网页视频,公考网课、教学课件、影视剧集、公开课、本地网页视频均可使用,学习追剧都能用。
-
多浏览器适配 依托Tampermonkey油猴插件运行,Chrome、Edge、Firefox等主流浏览器全都支持,安全无广告弹窗。
二、超简单上手步骤
- 浏览器应用商店搜索安装篡改猴(Tampermonkey) 插件
- 将本调速脚本导入插件内并启用
- 打开任意网页视频页面,即可调用调速功能自由变速
三、个性化调速修改小技巧
默认加减为0.5倍速,想调整精细度只需简单编辑脚本:
- 打开油猴插件,找到该调速脚本点击编辑
- 找到调速函数代码,修改加减后方数值即可
- 精细慢看:改为0.25,微小幅度调整节奏
- 大幅提速:改为1.0,快速跳过冗余片段
- 修改完成保存,刷新页面立刻生效
同时还能修改最大、最小倍速限制,打造专属自己的变速规则。
四、适用人群&使用场景
✅ 备考学习者:网课倍速听课,提速节省学习时间,难点慢速反复回看 ✅ 追剧爱好者:灵活调速追剧,跳过拖沓剧情 ✅ 办公剪辑人员:视频素材慢放细节核对,快进筛选片段
五、总结
小巧轻便无冗余功能,纯实用向调速脚本。摆脱平台固定倍速束缚,自主掌控视频播放节奏,简单几步就能解锁灵活变速体验,大幅提升线上观看、学习效率,日常上网必备实用小工具!
脚本
当前脚本是在坚果云上存储进度,方便在不同电脑间进行同步,如果你只有一台电脑,不需要同步的事情,那就让AI改写为本地存储进度的方式
// ==UserScript==
// @name 公考网课进度同步+视频加速
// @namespace http://tampermonkey.net/
// @version 5.0
// @description 粘贴完整视频链接即可自动识别课程文件夹,无需手动处理路径
// @author 豆包
// @match *://pan.baidu.com/pfile/video*
// @icon https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/icons/bar-chart-line.svg
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @grant GM_log
// @grant GM_setValue
// @grant GM_getValue
// @run-at document-start
// ==/UserScript==
(function () {
'use strict';
let COURSES = GM_getValue("user_courses", []);
let hiddenCourseKeys = GM_getValue("hiddenCourseKeys", []);
let showHiddenPanel = false;
// ==================== 坚果云配置 ====================
const WEBDAV_ROOT = "https://dav.jianguoyun.com/dav/";
const ACCOUNT = "替换为你的";
const AUTH_TOKEN = "替换为你的";
const SAVE_DIR = "tampermonkey_sync/";
const JSON_FILE = "progress.json";
const auth = btoa(unescape(encodeURIComponent(ACCOUNT + ":" + AUTH_TOKEN)));
// ==================== 视频加速配置 ====================
let currentSpeed = 1.0;
const MAX_SPEED = 16;
const MIN_SPEED = 0.5;
// ==================== 工具函数 ====================
function formatTime(seconds) {
const totalMin = Math.round(seconds / 60);
const h = Math.floor(totalMin / 60);
const m = totalMin % 60;
return `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}`;
}
function getVideoTime() {
const v = document.querySelectorAll('video');
return v.length ? v[0].currentTime || 0 : 0;
}
function getCurrentCourse() {
const url = location.href;
return COURSES.find(c => url.includes(c.pathFlag)) || null;
}
function toast(msg) {
let t = document.getElementById('syncToast');
if (t) t.remove();
let d = document.createElement('div');
d.id = 'syncToast'; d.textContent = msg;
document.body.appendChild(d);
setTimeout(() => d.style.opacity = 0, 2000);
setTimeout(() => d.remove(), 2500);
}
function saveCoursesToLocal() {
GM_setValue("user_courses", COURSES);
renderAllList();
}
// ==================== 🔥 核心:自动解析百度网盘文件夹路径 ====================
function autoParsePathFromUrl(url) {
try {
// 1. 找到 path= 后面的全部内容
const pathParam = new URL(url).searchParams.get('path');
if (!pathParam) return '';
// 2. 按 / 分割
const parts = pathParam.split('/').filter(p => p);
// 3. 去掉最后一个(视频文件),重新拼接成文件夹路径
const folderParts = parts.slice(0, -1);
return '/' + folderParts.join('/') + '/';
} catch (e) {
return '';
}
}
// ==================== 坚果云同步 ====================
async function ensureDir() {
return new Promise(resolve => {
GM_xmlhttpRequest({
method: "MKCOL", url: WEBDAV_ROOT + SAVE_DIR,
headers: { Authorization: "Basic " + auth },
onload: () => resolve(true), onerror: () => resolve(true)
});
});
}
function loadProgressJson() {
return new Promise(resolve => {
GM_xmlhttpRequest({
method: "GET", url: WEBDAV_ROOT + SAVE_DIR + JSON_FILE,
headers: { Authorization: "Basic " + auth },
onload: r => {
try {
resolve(r.status === 200 && r.responseText.trim() ? JSON.parse(r.responseText) : {});
} catch (e) {
resolve({});
}
},
onerror: () => resolve({})
});
});
}
async function saveProgress() {
let cur = getCurrentCourse();
if (!cur) return toast("⚠️ 当前页面不属于已添加的课程");
await ensureDir();
let data = await loadProgressJson();
data[cur.key] = { url: location.href, time: getVideoTime() };
GM_xmlhttpRequest({
method: "PUT", url: WEBDAV_ROOT + SAVE_DIR + JSON_FILE,
headers: { Authorization: "Basic " + auth, "Content-Type": "application/json" },
data: JSON.stringify(data, null, 2),
onload: resp => {
if (resp.status >= 200 && resp.status < 300) {
toast(`✅ 已保存\n${cur.name} ${formatTime(data[cur.key].time)}`);
renderAllList();
} else toast(`❌ 保存失败`);
},
onerror: () => toast("⚠️ 网络异常")
});
}
async function openCourse(course) {
let data = await loadProgressJson();
let item = data[course.key];
if (!item || !item.url) return toast(`🆘 ${course.name} 无进度记录`);
window.open(item.url, '_blank');
toast(`✅ 打开 ${course.name}`);
if (course.lectureUrl) setTimeout(() => {
window.open(course.lectureUrl, '_blank');
toast(`📖 打开讲义`)
}, 600);
}
// ==================== 课程收纳管理 ====================
function hideCourse(key) {
if (!hiddenCourseKeys.includes(key)) {
hiddenCourseKeys.push(key);
GM_setValue("hiddenCourseKeys", hiddenCourseKeys);
renderAllList();
}
}
function showCourse(key) {
hiddenCourseKeys = hiddenCourseKeys.filter(k => k !== key);
GM_setValue("hiddenCourseKeys", hiddenCourseKeys);
renderAllList();
}
function toggleHiddenPanel() {
showHiddenPanel = !showHiddenPanel;
renderAllList();
}
// ==================== 视频调速 ====================
function getVideoElement() {
return document.querySelector('video');
}
function unlockVideoSpeed() {
const video = getVideoElement();
if (!video) return;
delete video.playbackRate;
Object.defineProperty(video, 'playbackRate', { writable: true, configurable: true });
}
function setVideoSpeed(speed) {
const video = getVideoElement();
if (!video) return toast("⚠️ 未找到视频元素");
speed = Math.max(MIN_SPEED, Math.min(MAX_SPEED, speed));
currentSpeed = parseFloat(speed.toFixed(1));
unlockVideoSpeed();
video.playbackRate = currentSpeed;
document.getElementById('speedDisplay').innerText = `${currentSpeed}x`;
toast(`⚡ 倍速:${currentSpeed}x`);
}
function speedUp() { setVideoSpeed(currentSpeed + 0.5); }
function speedDown() { setVideoSpeed(currentSpeed - 0.5); }
function speed2x() { setVideoSpeed(2.0); }
function speedReset() { setVideoSpeed(1.0); }
// ==================== 课程管理弹窗 ====================
let editCourseIndex = -1;
function openCourseModal(editIndex = -1) {
editCourseIndex = editIndex;
const modal = document.getElementById('courseModal');
const title = document.getElementById('modalTitle');
const nameInput = document.getElementById('courseName');
const pathInput = document.getElementById('coursePath');
const keyInput = document.getElementById('courseKey');
const lectureInput = document.getElementById('courseLecture');
if (editIndex >= 0) {
title.innerText = "编辑课程";
const course = COURSES[editIndex];
nameInput.value = course.name || "";
pathInput.value = course.pathFlag || "";
keyInput.value = course.key || "";
lectureInput.value = course.lectureUrl || "";
} else {
title.innerText = "添加新课程";
nameInput.value = "";
pathInput.value = "";
keyInput.value = "course_" + Date.now();
lectureInput.value = "";
}
modal.style.display = "block";
}
function closeCourseModal() {
document.getElementById('courseModal').style.display = "none";
editCourseIndex = -1;
}
function saveCourse() {
const name = document.getElementById('courseName').value.trim();
let pathFlag = document.getElementById('coursePath').value.trim();
const key = document.getElementById('courseKey').value.trim();
const lectureUrl = document.getElementById('courseLecture').value.trim();
if (!name || !pathFlag || !key) {
return toast("⚠️ 课程名称/路径标识/存储key 不能为空!");
}
// 自动解析用户粘贴的完整链接
const autoPath = autoParsePathFromUrl(pathFlag);
if (autoPath) {
pathFlag = autoPath;
}
const course = { name, pathFlag, key, lectureUrl };
if (editCourseIndex >= 0) {
COURSES[editCourseIndex] = course;
toast("✅ 课程编辑成功!");
} else {
COURSES.push(course);
toast("✅ 课程添加成功!");
}
saveCoursesToLocal();
closeCourseModal();
}
function deleteCourse(index) {
if (!confirm("⚠️ 确定删除该课程?")) return;
COURSES.splice(index, 1);
saveCoursesToLocal();
toast("🗑️ 课程删除成功!");
}
// ==================== 渲染列表 ====================
async function renderAllList() {
const mainBox = document.getElementById('mainCourseList');
const hiddenBox = document.getElementById('hiddenCourseList');
const toggleBtn = document.getElementById('toggleHiddenBtn');
const allData = await loadProgressJson();
const mainCourses = COURSES.filter(item => !hiddenCourseKeys.includes(item.key));
const hideCourses = COURSES.filter(item => hiddenCourseKeys.includes(item.key));
let mainHtml = "";
mainCourses.forEach((course, index) => {
let time = allData[course.key] ? formatTime(allData[course.key].time || 0) : "未记录";
mainHtml += `
<div class="course-line">
<button class="course-normal-btn" data-key="${course.key}">${course.name}(${time})</button>
<button class="edit-btn" data-index="${index}">编辑</button>
<button class="del-btn" data-index="${index}">删除</button>
<button class="move-hide-btn" data-key="${course.key}">收纳</button>
</div>
`;
});
mainBox.innerHTML = mainHtml || '<div style="color:#999;padding:4px">暂无课程,点击上方添加课程</div>';
let hideHtml = "";
hideCourses.forEach((course, index) => {
let time = allData[course.key] ? formatTime(allData[course.key].time || 0) : "未记录";
hideHtml += `
<div class="course-line">
<button class="course-normal-btn" data-key="${course.key}">${course.name}(${time})</button>
<button class="move-show-btn" data-key="${course.key}">取出</button>
</div>
`;
});
hiddenBox.innerHTML = hideHtml;
toggleBtn.innerText = showHiddenPanel ? "收起隐藏课程" : `显示隐藏课程(${hideCourses.length}门)`;
hiddenBox.style.display = showHiddenPanel ? "block" : "none";
document.querySelectorAll('.course-normal-btn').forEach(btn => {
btn.onclick = () => {
let k = btn.dataset.key;
let target = COURSES.find(c => c.key === k);
openCourse(target);
}
});
document.querySelectorAll('.edit-btn').forEach(btn => { btn.onclick = () => openCourseModal(btn.dataset.index); });
document.querySelectorAll('.del-btn').forEach(btn => { btn.onclick = () => deleteCourse(btn.dataset.index); });
document.querySelectorAll('.move-hide-btn').forEach(btn => { btn.onclick = () => hideCourse(btn.dataset.key); });
document.querySelectorAll('.move-show-btn').forEach(btn => { btn.onclick = () => showCourse(btn.dataset.key); });
}
// ==================== 样式 ====================
GM_addStyle(`
#studyPanel{position:fixed;z-index:999999;width:320px;background:#fff;border-radius:10px;box-shadow:0 3px 15px rgba(0,0,0,.1);padding:12px;font-size:14px;user-select:none;cursor:grab;top:20px;right:20px}
#studyPanel:active{cursor:grabbing}
/* 按钮同行 2:1 布局 */
.btn-row{display:flex;gap:8px;margin-bottom:10px}
#saveBtn{flex:2;height:42px;border:none;border-radius:6px;color:#fff;font-size:15px;cursor:pointer;background:#007BFF}
#saveBtn:hover{background:#0056b3}
#addCourseBtn{flex:1;height:42px;border:none;border-radius:6px;color:#fff;font-size:15px;cursor:pointer;background:#13ce66}
#addCourseBtn:hover{background:#0fad56}
.speed-panel{display:flex;gap:4px;margin:8px 0;padding:8px;background:#f8f9fa;border-radius:6px}
#speedDisplay{flex:1;height:36px;line-height:36px;text-align:center;background:#e9ecef;border-radius:4px;font-weight:bold;color:#333}
.speed-btn{flex:1;height:36px;border:none;border-radius:4px;color:#fff;cursor:pointer;font-weight:bold}
.speed-up{background:#28c76f}
.speed-down{background:#ff6b35}
.speed-2x{background:#722ed1}
.speed-reset{background:#6c757d}
.title{margin:6px 0;font-weight:bold;color:#666}
.course-line{display:flex;gap:3px;margin:4px 0;flex-wrap:wrap}
.course-normal-btn{flex:1;height:34px;border:none;border-radius:4px;background:#f7f7f7;color:#333;text-align:left;padding:0 8px;cursor:pointer}
.course-normal-btn:hover{background:#e6f7ff;color:#007BFF}
.edit-btn{width:45px;height:34px;border:none;border-radius:4px;background:#ff9500;color:#fff;cursor:pointer;font-size:12px}
.del-btn{width:45px;height:34px;border:none;border-radius:4px;background:#f56c6c;color:#fff;cursor:pointer;font-size:12px}
.move-hide-btn{width:45px;height:34px;border:none;border-radius:4px;background:#ff6b35;color:#fff;cursor:pointer;font-size:12px}
.move-show-btn{width:45px;height:34px;border:none;border-radius:4px;background:#28c76f;color:#fff;cursor:pointer;font-size:12px}
#toggleHiddenBtn{width:100%;height:36px;margin-top:8px;border:none;border-radius:6px;background:#6c757d;color:#fff;cursor:pointer}
#hiddenCourseList{margin-top:6px;padding-top:6px;border-top:1px dashed #ddd}
.modal{display:none;position:fixed;z-index:9999999;left:0;top:0;width:100%;height:100%;background:rgba(0,0,0,0.5)}
.modal-content{background:#fff;margin:15% auto;padding:20px;border-radius:8px;width:300px}
.modal-title{font-weight:bold;margin-bottom:10px}
.modal-input{width:100%;height:36px;margin:5px 0;padding:0 8px;border:1px solid #ddd;border-radius:4px}
.modal-btns{display:flex;gap:8px;margin-top:10px}
.modal-btn{flex:1;height:36px;border:none;border-radius:4px;cursor:pointer}
.save-btn{background:#13ce66;color:#fff}
.close-btn{background:#6c757d;color:#fff}
#syncToast{position:fixed;z-index:9999999;bottom:30px;left:50%;transform:translateX(-50%);background:#333;color:#fff;padding:10px 20px;border-radius:6px;font-size:14px;opacity:1;transition:opacity 0.5s ease;white-space:pre-line;}
`);
// ==================== 面板DOM ====================
const panel = document.createElement('div');
panel.id = "studyPanel";
panel.innerHTML = `
<div class="btn-row">
<button id="saveBtn">📌 保存进度</button>
<button id="addCourseBtn">➕ 新课</button>
</div>
<div class="title">⚡ 视频倍速控制</div>
<div class="speed-panel">
<div id="speedDisplay">1.0x</div>
<button class="speed-btn speed-up">+</button>
<button class="speed-btn speed-down">-</button>
<button class="speed-btn speed-2x">2x</button>
<button class="speed-btn speed-reset">重置</button>
</div>
<div class="title">在用课程(时:分)</div>
<div id="mainCourseList"></div>
<button id="toggleHiddenBtn">显示隐藏课程</button>
<div id="hiddenCourseList"></div>
`;
const modal = document.createElement('div');
modal.id = "courseModal";
modal.className = "modal";
modal.innerHTML = `
<div class="modal-content">
<div class="modal-title" id="modalTitle">添加新课程</div>
<input class="modal-input" id="courseName" placeholder="课程名称(必填)">
<input class="modal-input" id="coursePath" placeholder="粘贴视频完整链接(自动解析)">
<input class="modal-input" id="courseKey" placeholder="存储key(自动生成)">
<input class="modal-input" id="courseLecture" placeholder="讲义链接(选填)">
<div class="modal-btns">
<button class="modal-btn save-btn">保存</button>
<button class="modal-btn close-btn">取消</button>
</div>
</div>
`;
// ==================== 加载 ====================
window.addEventListener('load', () => {
document.body.appendChild(panel);
document.body.appendChild(modal);
document.querySelector('.speed-up').onclick = speedUp;
document.querySelector('.speed-down').onclick = speedDown;
document.querySelector('.speed-2x').onclick = speed2x;
document.querySelector('.speed-reset').onclick = speedReset;
setVideoSpeed(1.0);
document.getElementById('addCourseBtn').onclick = () => openCourseModal();
document.querySelector('.save-btn').onclick = saveCourse;
document.querySelector('.close-btn').onclick = closeCourseModal;
document.getElementById("saveBtn").onclick = saveProgress;
document.getElementById("toggleHiddenBtn").onclick = toggleHiddenPanel;
});
// 拖拽
function makeDraggable(el) {
let drag = false, x1, y1;
el.addEventListener("mousedown", e => {
if (e.target.tagName === "BUTTON") return;
drag = true; x1 = e.clientX - el.offsetLeft; y1 = e.clientY - el.offsetTop;
el.style.cursor = "grabbing"; e.preventDefault();
});
document.addEventListener("mousemove", e => {
if (!drag) return;
el.style.left = (e.clientX - x1) + "px";
el.style.top = (e.clientY - y1) + "px";
el.style.right = "auto";
});
document.addEventListener("mouseup", () => { drag = false; el.style.cursor = "grab"; });
}
setTimeout(() => {
renderAllList();
makeDraggable(panel);
}, 1000);
})();