Greasy Fork is available in English.
Skip video intros and endings with customizable durations on multiple websites.
// ==UserScript==
// @name Universal Video Skipper | 全站通用跳过片头片尾自定义时长
// @namespace http://tampermonkey.net/
// @version 1.1
// @description Skip video intros and endings with customizable durations on multiple websites.
// @author MarySue
// @match *://*/*
// @grant none
// @run-at document-idle
// @license GNU GPLv3
// ==/UserScript==
(function() {
'use strict';
// 用户自定义块开始 -----------------------------------------------
const userSettings = {
settings: [
{
pattern: '^https?://(www\\.)?bilibili\\.com/bangumi/play/ep\\d+', // 匹配带有可变ep编号的Bilibili番剧 URL
introDuration: 95, // 片头时长 (秒)
outroDuration: 0, // 片尾时长 (秒)
forwardSeconds: 80, // 快进秒数
forwardKey: 'KeyK' // 快进快捷键
},
/*
{
pattern: '^https?://v\\.qq\\.com/x/cover/[a-zA-Z0-9]+/([a-zA-Z0-9]+)\\.html$', // 匹配带有可变video ID的腾讯视频URL
introDuration: 60, // 示例片头时长 (秒),请根据实际情况调整
outroDuration: 10, // 示例片尾时长 (秒),请根据实际情况调整
forwardSeconds: 30, // 示例快进秒数,请根据实际情况调整
forwardKey: 'KeyL' // 示例快进快捷键,请根据实际情况调整
}
*/
],
defaultSetting: {
introDuration: 0,
outroDuration: 0,
forwardSeconds: 0,
forwardKey: 'Space'
}
};
// 用户自定义块结束 --------------------------------------------
// 获取当前页面对应的设置
function getSettingsForPage(userSettings) {
let pageUrl = window.location.href;
for (let setting of userSettings.settings) {
if (new RegExp(setting.pattern).test(pageUrl)) {
return setting;
}
}
return userSettings.defaultSetting;
}
// 应用跳过逻辑到视频元素上
function applySkipLogic(videoElement, settings) {
let originalDuration = videoElement.duration;
let userSeeking = false;
videoElement.addEventListener('loadedmetadata', () => {
originalDuration = videoElement.duration;
});
// 监听用户是否正在拖动进度条
videoElement.addEventListener('seeking', () => {
userSeeking = true;
});
// 监听拖动完成
videoElement.addEventListener('seeked', () => {
userSeeking = false;
});
videoElement.addEventListener('timeupdate', () => {
if (!userSeeking && videoElement.currentTime < settings.introDuration) {
// 如果不是用户手动操作且当前时间小于片头时长,则跳过片头
videoElement.currentTime = settings.introDuration;
} else if (!userSeeking && originalDuration - videoElement.currentTime < settings.outroDuration) {
// 如果不是用户手动操作且接近片尾,则跳过片尾
videoElement.currentTime = originalDuration - settings.outroDuration;
}
});
}
// 监听键盘事件以实现快进
function setupKeyboardShortcut(settings) {
document.addEventListener('keydown', event => {
if (event.code === settings.forwardKey) {
let video = document.querySelector('video');
if (video) {
video.currentTime += settings.forwardSeconds;
}
}
});
}
// 动态检测视频元素的加载
function waitForVideoElement(callback) {
let intervalId = setInterval(() => {
let video = document.querySelector('video');
if (video) {
clearInterval(intervalId);
callback(video);
}
}, 500); // 每500毫秒检查一次
}
// 初始化
let settingsForPage = getSettingsForPage(userSettings);
// 确保脚本在页面完全加载后执行,并等待视频元素加载完成
waitForVideoElement((video) => {
applySkipLogic(video, settingsForPage);
setupKeyboardShortcut(settingsForPage);
});
})();
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.