// ==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);
}
})();