[Anich动漫]播放增强工具

旨在让Anich动画网的播放器变得更人性化,主要添加了: 快捷键控制和允许点击播放器任意空白元素实现暂停/播放

// ==UserScript==
// @name         [Anich动漫]播放增强工具
// @namespace    http://tampermonkey.net/
// @version      0.1.0
// @description  旨在让Anich动画网的播放器变得更人性化,主要添加了: 快捷键控制和允许点击播放器任意空白元素实现暂停/播放
// @author       Aomine
// @match        https://anich.emmmm.eu.org/*
// @icon         https://anich.emmmm.eu.org/favicon.ico
// @grant        GM_registerMenuCommand
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @license      GPL License
// ==/UserScript==

(function() {
    // 生成唯一状态标识符(基于当前完整路径)
    const getPathKey = () => `refreshed_${location.pathname.replace(/\//g, '_')}`;

    // 判断是否基础路径
    const isBasePath = () => /^\/b\/\d+$/.test(location.pathname);

    // 判断是否子页面路径
    const isSubPath = () => /^\/b\/\d+\/\d+$/.test(location.pathname);

    // 核心刷新逻辑
    const checkRefresh = () => {
        const storageKey = getPathKey();

        // 仅当从基础路径跳转到子路径时触发
        if (isSubPath() && !sessionStorage.getItem(storageKey)) {
            sessionStorage.setItem(storageKey, 'true');
            console.log('检测到子路径切换,触发刷新');
            location.reload();
        }
    };

    // 劫持History API
    const injectHistoryListener = () => {
        const _wr = function(type) {
            const orig = history[type];
            return function() {
                const rv = orig.apply(this, arguments);
                const event = new Event(type.toLowerCase());
                event.arguments = arguments;
                window.dispatchEvent(event);
                return rv;
            };
        };
        history.pushState = _wr('pushState');
        history.replaceState = _wr('replaceState');
    };

    // 初始化
    injectHistoryListener();

    // 监听所有路由变化方式
    window.addEventListener('pushstate', checkRefresh);
    window.addEventListener('replacestate', checkRefresh);
    window.addEventListener('popstate', checkRefresh);

    // 初始检查
    checkRefresh();
})();

(function() {
    'use strict';

    // 添加提示样式
    GM_addStyle(`
.anich-helper-notification {
    position: fixed;
    top: 20px;
    left: 50%;
    transform: translateX(-50%);
    background: rgba(0, 0, 0, 0.85);
    color: white;
    padding: 12px 20px;
    border-radius: 4px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
    z-index: 9999;
    max-width: 300px;
    animation: fadeIn 0.3s ease-out;
    font-family: Arial, sans-serif;
    font-size: 14px;
    line-height: 1.5;
    text-align: center;
}
    `);

// 修改通知显示函数
function showNotification(message, duration = 3000) {
    const notification = document.createElement('div');
    notification.className = 'anich-helper-notification';
    notification.innerHTML = message; // 改为innerHTML

    document.body.appendChild(notification);

    setTimeout(() => {
        notification.style.opacity = '0';
        notification.style.transition = 'opacity 0.3s ease-out';
        setTimeout(() => notification.remove(), 300);
    }, duration);
}
    // 初始化设置
    const autoWidescreenKey = 'autoWidescreen';
    const defaultSetting = false;

    // 获取或初始化设置
    let autoWidescreen = GM_getValue(autoWidescreenKey, defaultSetting);

// 修改菜单命令部分
GM_registerMenuCommand(`自动宽屏: ${autoWidescreen ? '开启' : '关闭'}`, function() {
    const newSetting = !autoWidescreen;
    GM_setValue(autoWidescreenKey, newSetting);
    autoWidescreen = newSetting;

    showNotification(
        `自动宽屏模式已${newSetting ? '开启' : '关闭'}<br>` +
        `此设置将在刷新后生效`
    );
});

    // 页面加载时检查是否需要自动开启(仅在刷新后执行)
    if (autoWidescreen) {
        let retryCount = 0;
        const maxRetries = 3;

        function safeEnableWidescreen() {
            try {
                const pagefitButton = document.querySelector('section[item][pagefit]');
                if (pagefitButton) {
                    pagefitButton.click();
                    console.log('已自动开启宽屏模式');
                } else if (retryCount < maxRetries) {
                    retryCount++;
                    setTimeout(safeEnableWidescreen, 1000 * retryCount);
                }
            } catch (error) {
                console.error('自动开启宽屏模式时出错:', error);
                if (retryCount < maxRetries) {
                    retryCount++;
                    setTimeout(safeEnableWidescreen, 1000 * retryCount);
                }
            }
        }

        setTimeout(safeEnableWidescreen, 1500);
    }

    // 1. 空格键控制播放/暂停
    document.addEventListener('keydown', function(e) {
        if (e.code === 'Space') {
            e.preventDefault();
            const playButton = document.querySelector('section[item][play], section[item][pause]');
            if (playButton) playButton.click();
        }
    });

    document.addEventListener('mousedown', function(e) {
        // 确保点击事件发生在播放器区域(例如 'section' 元素),且没有点击播放/暂停按钮
        const player = document.querySelector('section[player]');
        const playButton = document.querySelector('section[item][play], section[item][pause]');

        // 判断点击目标是否在播放器区域
        if (player && playButton && !playButton.contains(e.target) && player.contains(e.target) && e.button === 0) {
            const playButton = document.querySelector('section[item][play], section[item][pause]');
            if (playButton) playButton.click();
        }
    });

    // 2. D键控制弹幕开关 - 匹配开启和关闭两种状态
    document.addEventListener('keydown', function(e) {
        if (e.code === 'KeyD' && e.target.tagName !== 'INPUT' && e.target.tagName !== 'TEXTAREA') {
            e.preventDefault();
            const danmakuButton = document.querySelector('section[player] section[item][danmaku-open], section[player] section[item][danmaku-disabled]');

            if (danmakuButton) {
                danmakuButton.click();

                // 获取当前弹幕的状态
                const isDanmakuOpen = danmakuButton.hasAttribute('danmaku-open');
                const message = isDanmakuOpen ? "弹幕关" : "弹幕开";

                // 显示反馈提示
                showFeedback(message);
            }
        }
    });

    // 3. W键控制网页全屏
    document.addEventListener('keydown', function(e) {
        if (e.code === 'KeyW' && e.target.tagName !== 'INPUT' && e.target.tagName !== 'TEXTAREA') {
            e.preventDefault();
            const pagefitButton = document.querySelector('section[item][pagefit]');
            if (pagefitButton) pagefitButton.click();
        }
    });

    // 4. F键或Enter键控制全屏
    document.addEventListener('keydown', function(e) {
        if ((e.code === 'KeyF' || e.code === 'Enter') && e.target.tagName !== 'INPUT' && e.target.tagName !== 'TEXTAREA') {
            e.preventDefault();
            const fullscreenButton = document.querySelector('section[item][fullsreen], section[item][exit-fullscreen]');
            if (fullscreenButton) fullscreenButton.click();
        }
    });

    // 5. PageDown键控制下一集
    document.addEventListener('keydown', function(e) {
        if (e.code === 'PageDown' && e.target.tagName !== 'INPUT' && e.target.tagName !== 'TEXTAREA') {
            e.preventDefault();
            const nextButton = document.querySelector('section[item][next]');
            if (nextButton) nextButton.click();
        }
    });

    //6. Ctrl+←/Ctrl+→控制快进快退90s:
    document.addEventListener('keydown', function(e) {
        // 只处理Ctrl+方向键的组合
        if (!e.ctrlKey || (e.code !== 'ArrowLeft' && e.code !== 'ArrowRight')) return;

        // 排除输入框的情况
        if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;

        // 获取视频元素
        const video = document.querySelector('video');
        if (!video) {
            console.warn('未找到video元素');
            return;
        }

        e.preventDefault(); // 阻止默认行为

        const seekAmount = 90; // 快进/快退的秒数
        let feedbackText = '';

        if (e.code === 'ArrowLeft') {
            // Ctrl+← 后退90秒
            video.currentTime = Math.max(0, video.currentTime - seekAmount);
            feedbackText = `后退90秒,当前时间: ${formatTime(video.currentTime)}`;
        } else if (e.code === 'ArrowRight') {
            // Ctrl+→ 快进90秒
            video.currentTime = Math.min(video.duration, video.currentTime + seekAmount);
            feedbackText = `快进90秒,当前时间: ${formatTime(video.currentTime)}`;
        }

        // 添加反馈提示
        showFeedback(feedbackText);
    });

    // 格式化时间的函数
    function formatTime(seconds) {
        const minutes = Math.floor(seconds / 60);
        const secs = Math.floor(seconds % 60);
        return `${minutes}:${secs < 10 ? '0' + secs : secs}`;
    }

    // 显示反馈提示的函数
    function showFeedback(text) {
        // 创建反馈提示的 DOM 元素
        const feedback = document.createElement('div');
        feedback.style.position = 'absolute';
        feedback.style.top = '50%';
        feedback.style.left = '50%';
        feedback.style.transform = 'translate(-50%, -50%)';
        feedback.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
        feedback.style.color = 'white';
        feedback.style.padding = '10px 20px';
        feedback.style.borderRadius = '5px';
        feedback.style.fontSize = '16px';
        feedback.style.fontWeight = 'bold';
        feedback.style.zIndex = '9999';
        feedback.innerText = text;

        // 获取播放器的父元素
        const playerSection = document.querySelector('section[player]');
        if (playerSection) {
            playerSection.style.position = 'relative'; // 确保播放器容器有相对定位
            playerSection.appendChild(feedback);

            // 2秒后移除反馈提示
            setTimeout(() => {
                feedback.remove();
            }, 2000);
        }
    }

    // 添加操作提示
    const style = document.createElement('style');
    style.textContent = `
        .shortcut-hint {
            position: fixed;
            bottom: 20px;
            right: 20px;
            background: rgba(0,0,0,0.7);
            color: white;
            padding: 10px;
            border-radius: 5px;
            font-family: Arial, sans-serif;
            font-size: 14px;
            z-index: 9999;
        }
    `;
    document.head.appendChild(style);

    // 创建快捷键提示元素(初始隐藏)
    const createHintElement = () => {
        const hint = document.createElement('div');
        hint.className = 'shortcut-hint';
        hint.style.display = 'none'; // 初始隐藏
        hint.style.opacity = '0';
        hint.style.transition = 'opacity 0.3s';
        hint.innerHTML = `
            <div>快捷键提示:</div>
            <div>空格: 播放/暂停</div>
            <div>D: 弹幕开关</div>
            <div>W: 网页全屏</div>
            <div>F/Enter: 全屏</div>
            <div>PageDown: 下一集</div>
            <div>Shift+?: 显示/隐藏本提示</div>
        `;
        document.body.appendChild(hint);
        return hint;
    };

    // 显示或隐藏提示(带淡入淡出动画)
    const toggleHint = (hint) => {
        if (hint.style.display === 'none') {
            // 显示提示
            hint.style.display = 'block';
            setTimeout(() => hint.style.opacity = '1', 10); // 小延迟确保过渡生效
        } else {
            // 隐藏提示
            hint.style.opacity = '0';
            setTimeout(() => hint.style.display = 'none', 300); // 等待过渡完成
        }
    };

    // 初始化提示元素
    const hint = createHintElement();

    // 监听 Shift+? 快捷键
    document.addEventListener('keydown', (e) => {
        if (e.shiftKey && e.key === '?') {
            e.preventDefault(); // 防止浏览器默认行为(如Chrome的帮助菜单)
            toggleHint(hint);
        }
    });
})();