BGM Anime Time Set

在BGM.tv的追番页面添加设置按钮,可以设置番剧的播放时间并排序

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         BGM Anime Time Set
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  在BGM.tv的追番页面添加设置按钮,可以设置番剧的播放时间并排序
// @author       age
// @match        https://bgm.tv/
// @match        https://bangumi.tv/
// @match        https://chii.in/
// @license       MIT
// ==/UserScript==

(function() {
    'use strict';

    // 常量定义
    const STORAGE_KEY = 'BGM_HOME_ANIME_TIME_SET_AGE';
    const SETTINGS_KEY = 'BGM_HOME_ANIME_TIME_SETTINGS_AGE';
    const EXPIRATION_DAYS = 200;
    const WEEK_DAYS = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];

    // 缓存DOM元素
    let cachedContainer = null;
    let cachedAnimeTimeData = null;
    let cachedSettings = null;

    // 添加CSS样式
    const style = document.createElement('style');
    style.textContent = `
        #Age-js01-button {
            margin-left: 5px;
            padding: 1px 5px;
            border-radius: 3px;
            border: none;
            font-size: 12px;
            cursor: pointer;
            background-color: #2e2e2e;
            color: #eee;
        }
        #Age-js01-button.past {
            background-color: #118FDD;
        }
        #Age-js01-button.future {
            background-color: #10C745;
        }
        #Age-js01-button.soon {
            background-color: #FF3333;
        }
        #Age-js01-dialog {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            padding: 15px;
            border-radius: 4px;
            box-shadow: 0 0 10px rgba(0,0,0,0.5);
            z-index: 9999;
            font-size: 14px;
            background-color: #333;
            color: #fff;
            min-width: 300px;
        }
        #Age-js01-select, #Age-js01-time {
            padding: 3px;
            border-radius: 3px;
            background-color: #444;
            color: #fff;
        }
        #Age-js01-time {
            margin-right: 8px;
        }
        #Age-js01-button-container {
            margin-top: 10px;
            text-align: right;
        }
        #Age-js01-save, #Age-js01-cancel, #Age-js01-clear {
            padding: 3px 8px;
            border-radius: 3px;
            border: none;
            font-size: 12px;
            cursor: pointer;
            background-color: #444;
            color: #fff;
        }
        #Age-js01-clear {
            margin-right: 8px;
        }
        #Age-js01-manager-button {
        margin-left: 1px;
        border-radius: 100px;
        border: none;
        font-size: 22px;
        cursor: pointer;
        background-color: #3f3e3f;
        color: #eee;
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        padding: 2px 6px;
        width: 25px;
        height: 40px;
        }
        #Age-js01-manager-modal {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            padding: 15px;
            border-radius: 4px;
            box-shadow: 0 0 10px rgba(0,0,0,0.5);
            z-index: 9999;
            background-color: #333;
            color: #fff;
            min-width: 300px;
            max-height: 80vh;
            overflow-y: auto;
        }
        #Age-js01-manager-modal h3 {
            margin-top: 0;
            border-bottom: 1px solid #555;
            padding-bottom: 5px;
        }
        #Age-js01-manager-modal button {
            padding: 3px 8px;
            border-radius: 3px;
            border: none;
            font-size: 12px;
            cursor: pointer;
            background-color: #444;
            color: #fff;
            margin: 5px;
        }
        #Age-js01-manager-modal-content {
            margin: 10px 0;
        }
        #Age-js01-storage-content {
            background-color: #444;
            padding: 10px;
            border-radius: 3px;
            max-height: 200px;
            overflow-y: auto;
            font-family: monospace;
            white-space: pre-wrap;
            word-wrap: break-word;
        }
        #Age-js01-timezone-select {
            padding: 3px;
            border-radius: 3px;
            background-color: #444;
            color: #fff;
            margin-left: 8px;
        }
        #Age-js01-show-style-select {
            padding: 3px;
            border-radius: 3px;
            background-color: #444;
            color: #fff;
            margin-left: 8px;
        }

        html[data-theme='light'] #Age-js01-button {
            background-color: #f5f5f5;
            color: #333;
        }
        html[data-theme='light'] #Age-js01-button.past {
            background-color: #AEEFFF;
        }
        html[data-theme='light'] #Age-js01-button.future {
            background-color: #AEFFB8;
        }
        html[data-theme='light'] #Age-js01-button.soon {
            background-color: #FFAAAA;
        }
        html[data-theme='light'] #Age-js01-dialog {
            background-color: white;
            color: #333;
        }
        html[data-theme='light'] #Age-js01-select,
        html[data-theme='light'] #Age-js01-time,
        html[data-theme='light'] #Age-js01-timezone-select,
        html[data-theme='light'] #Age-js01-show-style-select {
            background-color: #fff;
            color: #333;
        }
        html[data-theme='light'] #Age-js01-save,
        html[data-theme='light'] #Age-js01-cancel,
        html[data-theme='light'] #Age-js01-clear {
            background-color: #f5f5f5;
            color: #333;
        }
        html[data-theme='light'] #Age-js01-manager-button {
            background-color: #fff;
            color: #333;
        }
        html[data-theme='light'] #Age-js01-manager-modal {
            background-color: white;
            color: #333;
        }
        html[data-theme='light'] #Age-js01-manager-modal h3 {
            border-bottom-color: #ddd;
        }
        html[data-theme='light'] #Age-js01-manager-modal button {
            background-color: #f5f5f5;
            color: #333;
        }
        html[data-theme='light'] #Age-js01-storage-content {
            background-color: #f5f5f5;
        }
    `;
    document.head.appendChild(style);

    // 数据存储操作
    function getAnimeTimeData() {
        if (cachedAnimeTimeData) return cachedAnimeTimeData;
        
        const data = localStorage.getItem(STORAGE_KEY);
        if (!data) return {};

        try {
            const parsed = JSON.parse(data);
            // 检查并清理过期数据
            const now = new Date();
            const cleanedData = {};
            
            for (const [id, entry] of Object.entries(parsed)) {
                if (entry.expiresAt && new Date(entry.expiresAt) > now) {
                    cleanedData[id] = {
                        weekDay: entry.weekDay,
                        time: entry.time
                    };
                }
            }
            
            cachedAnimeTimeData = cleanedData;
            return cachedAnimeTimeData;
        } catch (e) {
            console.error('Failed to parse anime time data:', e);
            return {};
        }
    }

    function setAnimeTimeData(data) {
        const now = new Date();
        const storageData = {};
        
        // 保留现有的过期时间,只更新修改的条目
        const existingData = getAnimeTimeData();
        
        for (const [id, entry] of Object.entries(data)) {
            storageData[id] = {
                ...entry,
                // 如果条目已存在且未被修改,保留原过期时间
                expiresAt: existingData[id] && existingData[id].weekDay === entry.weekDay && existingData[id].time === entry.time 
                    ? existingData[id].expiresAt 
                    : new Date(now.getTime() + EXPIRATION_DAYS * 24 * 60 * 60 * 1000).toISOString()
            };
        }
        
        localStorage.setItem(STORAGE_KEY, JSON.stringify(storageData));
        cachedAnimeTimeData = data;
    }

    function getSettings() {
        if (cachedSettings) return cachedSettings;
        
        const settings = localStorage.getItem(SETTINGS_KEY);
        if (!settings) return getDefaultSettings();
        
        try {
            const parsed = JSON.parse(settings);
            cachedSettings = validateSettings(parsed);
            return cachedSettings;
        } catch (e) {
            console.error('Failed to parse settings:', e);
            return getDefaultSettings();
        }
    }

    function getDefaultSettings() {
        return { 
            setShow: true, 
            showStyleRed: 0,
            showStyleGreen: 0,
            showStyleBlue: 0 
        };
    }

    function validateSettings(settings) {
        // 确保设置存在且是有效范围内的数字
        if (typeof settings.showStyleRed !== 'number' || settings.showStyleRed < 0 || settings.showStyleRed > 3) {
            settings.showStyleRed = 0;
        }
        if (typeof settings.showStyleGreen !== 'number' || settings.showStyleGreen < 0 || settings.showStyleGreen > 5) {
            settings.showStyleGreen = 0;
        }
        if (typeof settings.showStyleBlue !== 'number' || settings.showStyleBlue < 0 || settings.showStyleBlue > 3) {
            settings.showStyleBlue = 0;
        }
        return settings;
    }

    function setSettings(settings) {
        localStorage.setItem(SETTINGS_KEY, JSON.stringify(settings));
        cachedSettings = settings;
    }

    // 主初始化函数
    function init() {
        cachedContainer = document.getElementById('cloumnSubjectInfo');
        if (!cachedContainer) return;

        // 添加管理按钮
        addManagerButton();

        // 初始化数据
        cachedAnimeTimeData = getAnimeTimeData();
        cachedSettings = getSettings();

        // 为每个番剧添加设置按钮
        addSetButtons();

        // 根据设置显示/隐藏SET按钮
        toggleSetButtons(cachedSettings.setShow);

        // 重新排序番剧列表
        sortAnimeList();
    }

    // 添加管理按钮
    function addManagerButton() {
        const prgManagerMode = document.getElementById('prgManagerMode');
        if (!prgManagerMode || document.getElementById('Age-js01-manager-button')) return;

        const managerButton = document.createElement('button');
        managerButton.id = 'Age-js01-manager-button';
        managerButton.innerHTML = '<div style="text-align:center;line-height:2;">⏰︎</div><div style="text-align:center;line-height:0;"></div>';
        managerButton.addEventListener('click', showManagerModal);
        prgManagerMode.appendChild(managerButton);
    }

    // 显示管理框
    function showManagerModal() {
        const modal = document.createElement('div');
        modal.id = 'Age-js01-manager-modal';
        
        modal.innerHTML = `
            <h3>时间管理</h3>
            <div id="Age-js01-manager-modal-content">
                <div>
                    <button id="Age-js01-auto-fetch">自动获取时间</button>
                    <select id="Age-js01-timezone-select">
                        <option value="-12">UTC-12</option>
                        <option value="-11">UTC-11</option>
                        <option value="-10">UTC-10</option>
                        <option value="-9">UTC-9</option>
                        <option value="-8">UTC-8</option>
                        <option value="-7">UTC-7</option>
                        <option value="-6">UTC-6</option>
                        <option value="-5">UTC-5</option>
                        <option value="-4">UTC-4</option>
                        <option value="-3">UTC-3</option>
                        <option value="-2">UTC-2</option>
                        <option value="-1">UTC-1</option>
                        <option value="0">UTC±0</option>
                        <option value="1">UTC+1</option>
                        <option value="2">UTC+2</option>
                        <option value="3">UTC+3</option>
                        <option value="4">UTC+4</option>
                        <option value="5">UTC+5</option>
                        <option value="6">UTC+6</option>
                        <option value="7">UTC+7</option>
                        <option value="8" selected>UTC+8</option>
                        <option value="9">UTC+9</option>
                        <option value="10">UTC+10</option>
                        <option value="11">UTC+11</option>
                        <option value="12">UTC+12</option>
                    </select>
                </div>
                <div style="margin-top: 15px;">
                    <h4>显示样式设置</h4>
                    <div>
                        <label for="Age-js01-show-style-red">红:</label>
                        <select id="Age-js01-show-style-red">
                            <option value="0">1小时内即将放送</option>
                            <option value="1">2小时内即将放送</option>
                            <option value="2">4小时内即将放送</option>
                            <option value="3">禁用</option>
                        </select>
                    </div>
                    <div style="margin-top: 5px;">
                        <label for="Age-js01-show-style-green">绿:</label>
                        <select id="Age-js01-show-style-green">
                            <option value="0">18小时内即将放送</option>
                            <option value="1">24小时内即将放送</option>
                            <option value="2">今天内即将放送</option>
                            <option value="3">明天6点前即将放送</option>
                            <option value="4">明天8点前即将放送</option>
                            <option value="5">禁用</option>
                        </select>
                    </div>
                    <div style="margin-top: 5px;">
                        <label for="Age-js01-show-style-blue">蓝:</label>
                        <select id="Age-js01-show-style-blue">
                            <option value="0">18小时内已经放送</option>
                            <option value="1">24小时内已经放送</option>
                            <option value="2">今天内已经放送</option>
                            <option value="3">禁用</option>
                        </select>
                    </div>
                </div>
                <button id="Age-js01-toggle-set">${cachedSettings.setShow ? '隐藏所有SET按钮' : '显示所有SET按钮'}</button>
                <div style="margin-top: 15px;">
                    <h4>存储内容 (${STORAGE_KEY})</h4>
                    <div id="Age-js01-storage-content" contenteditable="true">${JSON.stringify(getFullStorageData(), null, 2)}</div>
                </div>
            </div>
            <div style="text-align: right;">
                <button id="Age-js01-save-storage">保存修改</button>
                <button id="Age-js01-close-manager">关闭</button>
            </div>
        `;

        // 设置当前显示样式
        modal.querySelector('#Age-js01-show-style-red').value = cachedSettings.showStyleRed;
        modal.querySelector('#Age-js01-show-style-green').value = cachedSettings.showStyleGreen;
        modal.querySelector('#Age-js01-show-style-blue').value = cachedSettings.showStyleBlue;

        document.body.appendChild(modal);

        // 使用事件委托处理模态框内的事件
        modal.addEventListener('click', (e) => {
            const target = e.target;
            
            if (target.id === 'Age-js01-auto-fetch') {
                autoFetchSchedule();
            } else if (target.id === 'Age-js01-toggle-set') {
                const newSettings = { ...cachedSettings, setShow: !cachedSettings.setShow };
                setSettings(newSettings);
                toggleSetButtons(newSettings.setShow);
                target.textContent = newSettings.setShow ? '隐藏所有SET按钮' : '显示所有SET按钮';
            } else if (target.id === 'Age-js01-save-storage') {
                try {
                    const newData = JSON.parse(document.getElementById('Age-js01-storage-content').textContent);
                    // 保留未修改条目的过期时间
                    const existingData = getFullStorageData();
                    const now = new Date();
                    
                    const mergedData = {};
                    for (const [id, entry] of Object.entries(newData)) {
                        mergedData[id] = {
                            ...entry,
                            expiresAt: existingData[id] && existingData[id].weekDay === entry.weekDay && existingData[id].time === entry.time
                                ? existingData[id].expiresAt
                                : new Date(now.getTime() + EXPIRATION_DAYS * 24 * 60 * 60 * 1000).toISOString()
                        };
                    }
                    
                    localStorage.setItem(STORAGE_KEY, JSON.stringify(mergedData));
                    cachedAnimeTimeData = getAnimeTimeData(); // 重新加载数据
                    addSetButtons();
                    sortAnimeList();
                    alert('保存成功');
                } catch (e) {
                    alert('保存失败: JSON格式错误');
                    console.error(e);
                }
            } else if (target.id === 'Age-js01-close-manager') {
                const newSettings = {
                    ...cachedSettings,
                    showStyleRed: parseInt(modal.querySelector('#Age-js01-show-style-red').value),
                    showStyleGreen: parseInt(modal.querySelector('#Age-js01-show-style-green').value),
                    showStyleBlue: parseInt(modal.querySelector('#Age-js01-show-style-blue').value)
                };
                setSettings(newSettings);
                addSetButtons();
                sortAnimeList();
                document.body.removeChild(modal);
            }
        });
    }

    // 获取完整的存储数据(包括过期时间)
    function getFullStorageData() {
        const data = localStorage.getItem(STORAGE_KEY);
        return data ? JSON.parse(data) : {};
    }

    // 自动获取时间表
    async function autoFetchSchedule() {
        if (!confirm('此操作将清空目前的时间表,是否继续?')) return;

        try {
            const subjectLinks = document.querySelectorAll('#cloumnSubjectInfo .infoWrapper_tv.hidden.clearit a[href^="/subject/"]');
            const subjectIds = new Set();

            subjectLinks.forEach(link => {
                const href = link.getAttribute('href');
                const match = href.match(/^\/subject\/(\d+)/);
                if (match && match[1]) subjectIds.add(match[1]);
            });

            if (subjectIds.size === 0) {
                alert('未找到任何动画条目ID');
                return;
            }

            const response = await fetch('https://raw.githubusercontent.com/zhollgit/bgm-onair/main/onair.json');
            if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);

            const data = await response.json();
            const newAnimeTimeData = {};
            const timezoneOffset = parseInt(document.getElementById('Age-js01-timezone-select')?.value || 8);
            const now = new Date();

            data.items.forEach(item => {
                const bangumiSite = item.sites.find(site => site.site === 'bangumi');
                if (bangumiSite?.id && subjectIds.has(bangumiSite.id) && item.begin) {
                    const beginDate = new Date(item.begin);
                    let adjustedHours = beginDate.getUTCHours() + timezoneOffset;
                    let adjustedDay = beginDate.getUTCDay();
                    
                    if (adjustedHours >= 24) {
                        adjustedHours -= 24;
                        adjustedDay = (adjustedDay + 1) % 7;
                    } else if (adjustedHours < 0) {
                        adjustedHours += 24;
                        adjustedDay = (adjustedDay - 1 + 7) % 7;
                    }
                    
                    const time = `${adjustedHours.toString().padStart(2, '0')}:${beginDate.getUTCMinutes().toString().padStart(2, '0')}`;
                    newAnimeTimeData[bangumiSite.id] = { 
                        weekDay: adjustedDay, 
                        time,
                        expiresAt: new Date(now.getTime() + EXPIRATION_DAYS * 24 * 60 * 60 * 1000).toISOString()
                    };
                }
            });
            
            localStorage.setItem(STORAGE_KEY, JSON.stringify(newAnimeTimeData));
            cachedAnimeTimeData = getAnimeTimeData();
            addSetButtons();
            sortAnimeList();

            const storageContent = document.getElementById('Age-js01-storage-content');
            if (storageContent) storageContent.textContent = JSON.stringify(newAnimeTimeData, null, 2);

            alert(`成功获取 ${Object.keys(newAnimeTimeData).length} 个番剧的时间数据`);
        } catch (error) {
            console.error('自动获取时间表失败:', error);
            alert('自动获取时间表失败: ' + error.message);
        }
    }

    // 切换SET按钮的显示/隐藏
    function toggleSetButtons(show) {
        document.querySelectorAll('#Age-js01-button').forEach(button => {
            const subjectId = button.getAttribute('data-subject-id');
            if (!cachedAnimeTimeData[subjectId]) {
                button.style.display = show ? '' : 'none';
            }
        });
    }

    // 添加设置按钮
    function addSetButtons() {
        const editLinks = cachedContainer.querySelectorAll('a.thickbox.l[id^="sbj_prg_"]:not([data-processed])');

        editLinks.forEach(editLink => {
            if (editLink.textContent.trim() === '[edit]') return;

            const subjectId = editLink.id.split('_')[2];
            editLink.setAttribute('data-processed', 'true');

            const setButton = document.createElement('button');
            setButton.id = 'Age-js01-button';
            setButton.setAttribute('data-subject-id', subjectId);
            
            if (cachedAnimeTimeData[subjectId]) {
                setButton.textContent = formatTimeData(cachedAnimeTimeData[subjectId]);
                const timeStatus = getTimeStatus(cachedAnimeTimeData[subjectId]);
                if (timeStatus) setButton.classList.add(timeStatus);
            } else {
                setButton.textContent = 'SET';
                if (!cachedSettings.setShow) setButton.style.display = 'none';
            }

            setButton.addEventListener('click', () => showTimeSettingDialog(subjectId, setButton));
            editLink.parentNode.insertBefore(setButton, editLink.nextSibling);
        });
    }

    // 获取时间状态
    function getTimeStatus(timeData) {
        const now = new Date();
        const today = now.getDay();
        const currentHours = now.getHours();
        const currentMinutes = now.getMinutes();

        const [hours, minutes] = timeData.time.split(':').map(Number);
        const targetDay = timeData.weekDay;

        let dayDiff = targetDay - today;
        if (dayDiff < -3) dayDiff += 7;
        else if (dayDiff > 3) dayDiff -= 7;

        const totalDiffHours = dayDiff * 24 + (hours - currentHours) + (minutes - currentMinutes) / 60;
        
        // 红色优先级最高
        if (cachedSettings.showStyleRed !== 3) {
            switch (cachedSettings.showStyleRed) {
                case 0: if (totalDiffHours >= 0 && totalDiffHours < 1) return 'soon'; break;
                case 1: if (totalDiffHours >= 0 && totalDiffHours < 2) return 'soon'; break;
                case 2: if (totalDiffHours >= 0 && totalDiffHours < 4) return 'soon'; break;
            }
        }
        
        // 然后检查绿色条件
        if (cachedSettings.showStyleGreen !== 5) {
            switch (cachedSettings.showStyleGreen) {
                case 0: if (totalDiffHours >= 0 && totalDiffHours < 18) return 'future'; break;
                case 1: if (totalDiffHours >= 0 && totalDiffHours < 24) return 'future'; break;
                case 2: if (dayDiff === 0 && totalDiffHours >= 0) return 'future'; break;
                case 3: if ((dayDiff === 0 && totalDiffHours >= 0) || (dayDiff === 1 && hours < 6)) return 'future'; break;
                case 4: if ((dayDiff === 0 && totalDiffHours >= 0) || (dayDiff === 1 && hours < 8)) return 'future'; break;
            }
        }
        
        // 最后检查蓝色条件
        if (cachedSettings.showStyleBlue !== 3) {
            switch (cachedSettings.showStyleBlue) {
                case 0: if (totalDiffHours >= -18 && totalDiffHours < 0) return 'past'; break;
                case 1: if (totalDiffHours >= -24 && totalDiffHours < 0) return 'past'; break;
                case 2: if (dayDiff === 0 && totalDiffHours < 0) return 'past'; break;
            }
        }
        
        return '';
    }

    // 显示时间设置对话框
    function showTimeSettingDialog(subjectId, button) {
        const dialog = document.createElement('div');
        dialog.id = 'Age-js01-dialog';

        const weekDaySelect = document.createElement('select');
        weekDaySelect.id = 'Age-js01-select';
        WEEK_DAYS.forEach((day, index) => {
            const option = document.createElement('option');
            option.value = index;
            option.textContent = day;
            weekDaySelect.appendChild(option);
        });

        const timeInput = document.createElement('input');
        timeInput.id = 'Age-js01-time';
        timeInput.type = 'time';

        if (cachedAnimeTimeData[subjectId]) {
            weekDaySelect.value = cachedAnimeTimeData[subjectId].weekDay;
            timeInput.value = cachedAnimeTimeData[subjectId].time;
        }

        dialog.innerHTML = `
            <label>星期: </label>
            <select id="Age-js01-select">${WEEK_DAYS.map((day, i) => 
                `<option value="${i}">${day}</option>`).join('')}</select>
            <label style="margin-left:8px;">时间: </label>
            <input id="Age-js01-time" type="time" ${cachedAnimeTimeData[subjectId] ? 
                `value="${cachedAnimeTimeData[subjectId].time}"` : ''}>
            <div id="Age-js01-button-container">
                <button id="Age-js01-save">保存</button>
                <button id="Age-js01-clear">清除</button>
                <button id="Age-js01-cancel">取消</button>
            </div>
        `;

        document.body.appendChild(dialog);

        // 事件处理
        dialog.addEventListener('click', (e) => {
            if (e.target.id === 'Age-js01-save') {
                const weekDay = parseInt(dialog.querySelector('#Age-js01-select').value);
                const time = dialog.querySelector('#Age-js01-time').value;

                if (!time) {
                    alert('请选择时间');
                    return;
                }

                // 获取当前存储的完整数据
                const fullData = getFullStorageData();
                const now = new Date();
                
                // 更新数据
                fullData[subjectId] = {
                    weekDay,
                    time,
                    expiresAt: new Date(now.getTime() + EXPIRATION_DAYS * 24 * 60 * 60 * 1000).toISOString()
                };
                
                // 保存更新
                localStorage.setItem(STORAGE_KEY, JSON.stringify(fullData));
                cachedAnimeTimeData = getAnimeTimeData();

                button.textContent = formatTimeData(cachedAnimeTimeData[subjectId]);
                button.className = 'Age-js01-button';
                const timeStatus = getTimeStatus(cachedAnimeTimeData[subjectId]);
                if (timeStatus) button.classList.add(timeStatus);

                sortAnimeList();
                document.body.removeChild(dialog);
            } else if (e.target.id === 'Age-js01-clear') {
                // 获取当前存储的完整数据
                const fullData = getFullStorageData();
                
                if (fullData[subjectId]) {
                    delete fullData[subjectId];
                    localStorage.setItem(STORAGE_KEY, JSON.stringify(fullData));
                    cachedAnimeTimeData = getAnimeTimeData();

                    button.textContent = 'SET';
                    button.className = 'Age-js01-button';
                    sortAnimeList();
                }
                document.body.removeChild(dialog);
            } else if (e.target.id === 'Age-js01-cancel') {
                document.body.removeChild(dialog);
            }
        });
    }

    // 格式化时间数据显示
    function formatTimeData(timeData) {
        return `${WEEK_DAYS[timeData.weekDay]} ${timeData.time}`;
    }

    // 排序
    function sortAnimeList() {
        const wrapper = cachedContainer.querySelector('.infoWrapperContainer.infoWrapper_tv.hidden.clearit');
        if (!wrapper) return;

        const animeItems = Array.from(wrapper.querySelectorAll('.clearit.infoWrapper'));

        animeItems.sort((a, b) => {
            const aId = a.id.split('_')[1];
            const bId = b.id.split('_')[1];
            const aData = cachedAnimeTimeData[aId];
            const bData = cachedAnimeTimeData[bId];

            if (!aData && !bData) return 0;
            if (!aData) return 1;
            if (!bData) return -1;
            if (aData.weekDay !== bData.weekDay) return aData.weekDay - bData.weekDay;
            return aData.time.localeCompare(bData.time);
        });

        // 使用文档片段减少重绘
        const fragment = document.createDocumentFragment();
        animeItems.forEach(item => fragment.appendChild(item));
        wrapper.appendChild(fragment);
    }

    // 使用DOMContentLoaded而不是load事件,加快响应速度
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        setTimeout(init, 0);
    }
})();