Greasy Fork

Greasy Fork is available in English.

Universal Video Skipper | 全站通用跳过片头片尾自定义时长

Skip video intros and endings with customizable durations on multiple websites.

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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/>.