KiwiSDR定时录音

KiwiSDR定时录音, 增强录音

// ==UserScript==
// @name         KiwiSDR定时录音
// @namespace    http://tampermonkey.net/
// @version      0.6
// @description  KiwiSDR定时录音, 增强录音
// @license      MIT
// @author       JerryXu09
// @match        http://*.proxy.kiwisdr.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // 日志管理器
    const Logger = {
        logs: [],
        log(message, type = 'info') {
            const timestamp = new Date().toLocaleString();
            const logEntry = { timestamp, type, message };
            this.logs.push(logEntry);
            console[type](`[KiwiSDR Auto Record] ${timestamp} - ${message}`);
        },
        getLogs() {
            return this.logs;
        },
        clearLogs() {
            this.logs = [];
        }
    };

    // UI管理器
    const UIManager = {
        createElement(type, properties = {}) {
            const element = document.createElement(type);
            Object.entries(properties).forEach(([key, value]) => {
                if (key === 'style') {
                    Object.assign(element.style, value);
                } else {
                    element[key] = value;
                }
            });
            return element;
        },
        createStyledContainer(properties = {}) {
            return this.createElement('div', {
                style: {
                    position: 'fixed',
                    top: '50px',
                    right: '10px',
                    background: '#f0f0f0',
                    border: '1px solid #ccc',
                    padding: '15px',
                    borderRadius: '8px',
                    boxShadow: '0 4px 6px rgba(0,0,0,0.1)',
                    zIndex: 1000,
                    display: 'none',
                    ...properties.style
                },
                ...properties
            });
        }
    };

    // 时间工具
    const TimeUtils = {
        formatDateTime(date) {
            const pad = (n) => n < 10 ? '0' + n : n;
            return date.getFullYear() + '-' +
                   pad(date.getMonth() + 1) + '-' +
                   pad(date.getDate()) + ' ' +
                   pad(date.getHours()) + ':' +
                   pad(date.getMinutes()) + ':' +
                   pad(date.getSeconds());
        },
        parseRelativeTime(input) {
            const now = new Date();
            const match = input.match(/^(\d+)\s*([hmd])$/i);
            if (!match) return null;

            const [, value, unit] = match;
            const numValue = parseInt(value, 10);

            switch (unit.toLowerCase()) {
                case 'h': return new Date(now.getTime() + numValue * 60 * 60 * 1000);
                case 'm': return new Date(now.getTime() + numValue * 60 * 1000);
                case 'd': return new Date(now.getTime() + numValue * 24 * 60 * 60 * 1000);
                default: return null;
            }
        },
        validateTimeInput(input) {
            // 同时支持绝对时间和相对时间
            const absoluteRegex = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/;
            const relativeRegex = /^\d+\s*[hmd]$/i;

            if (absoluteRegex.test(input)) {
                return new Date(input.replace(/-/g, '/'));
            } else if (relativeRegex.test(input)) {
                return this.parseRelativeTime(input);
            }

            return null;
        }
    };

    // KiwiSDR操作管理器
    const KiwiSDRManager = {
        getButton(selector) {
            const button = document.querySelector(selector);
            if (!button) {
                Logger.log(`按钮 "${selector}" 未找到`, 'warn');
                return null;
            }
            return button;
        },
        clickButton(button) {
            if (button) {
                button.click();
                Logger.log('按钮已点击');
                return true;
            }
            return false;
        },
        findRecordButton() {
            return this.getButton('.id-rec1');
        },
        findSaveWFButton() {
            return this.getButton('.id-btn-grp-56');
        }
    };

    // 主定时录制管理器
    const RecordScheduler = {
        activeTimer: null,
        recordTask: null,
        
        scheduleRecording(startTime, endTime, saveWF = false) {
            // 取消之前可能存在的定时任务
            this.cancelSchedule();

            const now = new Date();
            const startDelay = startTime - now;
            const stopDelay = endTime - now;

            if (startDelay <= 0 || stopDelay <= 0) {
                Logger.log('无效的时间设置', 'error');
                return false;
            }

            Logger.log(`定时录制已安排:开始于 ${startTime.toLocaleString()},结束于 ${endTime.toLocaleString()}`);

            this.recordTask = {
                startTime,
                endTime,
                saveWF
            };

            this.activeTimer = setTimeout(() => {
                const recordButton = KiwiSDRManager.findRecordButton();
                if (recordButton) {
                    KiwiSDRManager.clickButton(recordButton);

                    // 定时停止录音
                    this.activeTimer = setTimeout(() => {
                        KiwiSDRManager.clickButton(recordButton);

                        if (saveWF) {
                            setTimeout(() => {
                                const saveWFButton = KiwiSDRManager.findSaveWFButton();
                                if (saveWFButton) {
                                    KiwiSDRManager.clickButton(saveWFButton);
                                    Logger.log('WF图像已保存');
                                } else {
                                    Logger.log('无法找到保存WF的按钮', 'warn');
                                }
                            }, 1000);
                        }

                        this.recordTask = null;
                        this.updateUI();
                    }, stopDelay - startDelay);

                    this.updateUI();
                } else {
                    Logger.log('无法找到录音按钮', 'error');
                }
            }, startDelay);

            return true;
        },
        cancelSchedule() {
            if (this.activeTimer) {
                clearTimeout(this.activeTimer);
                this.activeTimer = null;
                this.recordTask = null;
                Logger.log('定时录制已取消');
                this.updateUI();
            }
        },
        updateUI() {
            const startButton = document.getElementById('kiwiSDRScheduleBtn');
            const statusSpan = document.getElementById('kiwiSDRStatus');
            
            if (this.recordTask) {
                startButton.disabled = true;
                startButton.innerText = '定时录制进行中';
                statusSpan.innerText = `录制预定:${this.recordTask.startTime.toLocaleString()}`;
            } else {
                startButton.disabled = false;
                startButton.innerText = '设置定时录制';
                statusSpan.innerText = '';
            }
        }
    };

    // 主UI创建
    function createRecordUI() {
        const container = UIManager.createElement('div', {
            style: {
                position: 'fixed',
                top: '10px',
                right: '10px',
                zIndex: 1000,
                display: 'flex',
                alignItems: 'center'
            }
        });

        const startButton = UIManager.createElement('button', {
            id: 'kiwiSDRScheduleBtn',
            innerText: '设置定时录制',
            style: {
                marginRight: '10px',
                padding: '5px 10px',
                backgroundColor: '#4CAF50',
                color: 'white',
                border: 'none',
                borderRadius: '4px',
                cursor: 'pointer'
            }
        });

        const statusSpan = UIManager.createElement('span', {
            id: 'kiwiSDRStatus',
            style: {
                color: '#666',
                fontSize: '12px'
            }
        });

        const modalContainer = UIManager.createStyledContainer({
            id: 'kiwiSDRModal'
        });

        modalContainer.innerHTML = `
            <div style="margin-bottom: 10px;">
                <label>开始时间(支持绝对或相对时间):</label>
                <input id="startTimeInput" type="text" placeholder="如:2h 或 2024-01-01 14:30:00" 
                       style="width: 200px; padding: 5px; margin-top: 5px;">
                <div style="color: #888; font-size: 12px;">
                    支持格式:相对时间(如1h, 30m)或绝对时间(2024-01-01 14:30:00)
                </div>
            </div>
            <div style="margin-bottom: 10px;">
                <label>结束时间(支持绝对或相对时间):</label>
                <input id="endTimeInput" type="text" placeholder="如:3h 或 2024-01-01 15:30:00" 
                       style="width: 200px; padding: 5px; margin-top: 5px;">
                <div style="color: #888; font-size: 12px;">
                    支持格式:相对时间(如2h, 45m)或绝对时间(2024-01-01 15:30:00)
                </div>
            </div>
            <div style="margin-bottom: 10px;">
                <label>
                    <input type="checkbox" id="saveWFCheckbox" checked> 
                    录音完成后保存WF图像
                </label>
            </div>
            <div>
                <button id="confirmButton" style="margin-right: 10px; padding: 5px 10px; background-color: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer;">
                    确认
                </button>
                <button id="cancelButton" style="padding: 5px 10px; background-color: #f44336; color: white; border: none; border-radius: 4px; cursor: pointer;">
                    取消
                </button>
            </div>
        `;

        startButton.addEventListener('click', () => {
            modalContainer.style.display = modalContainer.style.display === 'none' ? 'block' : 'none';
        });

        const confirmButton = modalContainer.querySelector('#confirmButton');
        const cancelButton = modalContainer.querySelector('#cancelButton');

        confirmButton.addEventListener('click', () => {
            const startInput = document.getElementById('startTimeInput').value.trim();
            const endInput = document.getElementById('endTimeInput').value.trim();
            const saveWF = document.getElementById('saveWFCheckbox').checked;

            const startTime = TimeUtils.validateTimeInput(startInput);
            const endTime = TimeUtils.validateTimeInput(endInput);

            if (!startTime || !endTime) {
                alert('请输入有效的开始和结束时间!');
                return;
            }

            const now = new Date();
            if (startTime <= now) {
                alert('开始时间必须在当前时间之后!');
                return;
            }

            if (endTime <= startTime) {
                alert('结束时间必须在开始时间之后!');
                return;
            }

            const success = RecordScheduler.scheduleRecording(startTime, endTime, saveWF);
            
            if (success) {
                modalContainer.style.display = 'none';
            }
        });

        cancelButton.addEventListener('click', () => {
            RecordScheduler.cancelSchedule();
            modalContainer.style.display = 'none';
        });

        container.appendChild(startButton);
        container.appendChild(statusSpan);
        document.body.appendChild(container);
        document.body.appendChild(modalContainer);
    }

    // 初始化
    function init() {
        createRecordUI();
        Logger.log('KiwiSDR定时录音脚本已加载');
    }

    // 页面加载完成后执行初始化
    if (document.readyState === 'complete') {
        init();
    } else {
        window.addEventListener('load', init);
    }
})();