80 lines
4.5 KiB
JavaScript
80 lines
4.5 KiB
JavaScript
'use strict';
|
|
const $ = id => document.getElementById(id);
|
|
let token = '', running = false, starting = false, loadingChannels = false, loadedKey = '', lastLog = -1;
|
|
const labels = {idle:'等待开始',running:'下载中',completed:'已完成',partial:'未达到目标',cancelled:'已停止',error:'任务中断'};
|
|
const key = () => $('key').value.trim();
|
|
function controls() {
|
|
const busy = running || starting;
|
|
$('start').disabled = busy || !loadedKey || loadedKey !== key() || !$('channel').value;
|
|
$('stop').disabled = !running;
|
|
$('channels').disabled = busy || loadingChannels;
|
|
for (const id of ['key','channel','target']) $(id).disabled = busy || (id === 'channel' && !loadedKey);
|
|
}
|
|
async function post(path, body) {
|
|
const response = await fetch('/api/' + path, {method:'POST',headers:{'Content-Type':'application/json','X-Session-Token':token},body:JSON.stringify(body)});
|
|
const data = await response.json();
|
|
if (!response.ok) throw new Error(data.error || '请求失败');
|
|
return data;
|
|
}
|
|
$('key').addEventListener('input', () => {loadedKey=''; $('channel').replaceChildren(new Option('请重新获取频道','')); controls();});
|
|
$('channels').addEventListener('click', async () => {
|
|
loadingChannels=true; controls(); $('notice').textContent='正在获取频道…';
|
|
const submittedKey = key();
|
|
try {
|
|
const data = await post('channels',{key:submittedKey});
|
|
if (key() !== submittedKey) return;
|
|
$('channel').replaceChildren(...data.channels.map(c => new Option(c,c)));
|
|
loadedKey=submittedKey; $('notice').textContent=`已获取 ${data.channels.length} 个频道。`;
|
|
$('debug-channel').value=$('channel').value;
|
|
} catch(error) {$('notice').textContent=error.message;}
|
|
finally {loadingChannels=false; controls();}
|
|
});
|
|
$('channel').addEventListener('change', () => {$('debug-channel').value=$('channel').value; controls();});
|
|
$('download-form').addEventListener('submit', async event => {
|
|
event.preventDefault(); starting=true; controls(); $('notice').textContent='';
|
|
try {await post('start',{key:key(),channel:$('channel').value,target:Number($('target').value)}); await refresh();}
|
|
catch(error) {$('notice').textContent=error.message;}
|
|
finally {starting=false; controls();}
|
|
});
|
|
$('stop').addEventListener('click', async () => {
|
|
$('stop').disabled=true;
|
|
try {await post('stop',{}); $('notice').textContent='正在停止,等待当前请求或文章保存结束…';}
|
|
catch(error) {$('notice').textContent=error.message;}
|
|
});
|
|
$('debug-form').addEventListener('submit', async event => {
|
|
event.preventDefault(); $('debug-submit').disabled=true; $('debug-output').textContent='正在请求…';
|
|
try {
|
|
const data=await post('debug',{key:key(),endpoint:$('endpoint').value,channel:$('debug-channel').value,num:Number($('debug-num').value),start:Number($('debug-start').value)});
|
|
const output=JSON.stringify(data,null,2);
|
|
$('debug-output').textContent=output.slice(0,100000)+(output.length>100000?'\n…输出超过 100000 字符,已截断。':'');
|
|
} catch(error) {$('debug-output').textContent=error.message;}
|
|
finally {$('debug-submit').disabled=false;}
|
|
});
|
|
async function refresh() {
|
|
const response=await fetch('/api/state');
|
|
if (!response.ok) throw new Error('无法读取下载状态');
|
|
const data=await response.json();
|
|
running=data.status==='running'; controls();
|
|
const percent=data.target ? Math.floor(data.saved/data.target*100) : 0;
|
|
$('status').textContent=labels[data.status] || data.status;
|
|
$('progress-text').textContent=`下载新闻 ${data.saved}/${data.target} 条`;
|
|
$('percent').textContent=`已完成 ${percent}%`; $('progress').value=percent;
|
|
for (const id of ['saved','skipped','failed','requests','message','output']) $(id).textContent=data[id];
|
|
const latest=data.logs.length ? data.logs[data.logs.length-1].id : 0;
|
|
if (latest!==lastLog) {
|
|
const logs=$('logs'); const bottom=logs.scrollHeight-logs.scrollTop-logs.clientHeight<40;
|
|
logs.replaceChildren(...data.logs.map(log => {const row=document.createElement('div'); const stamp=document.createElement('time'); stamp.textContent=log.time; row.append(stamp,document.createTextNode(log.message)); return row;}));
|
|
if (bottom) logs.scrollTop=logs.scrollHeight;
|
|
lastLog=latest;
|
|
}
|
|
}
|
|
async function poll() {
|
|
try {await refresh();}
|
|
catch(error) {$('notice').textContent='与本地服务连接中断,请检查启动窗口。';}
|
|
setTimeout(poll,700);
|
|
}
|
|
(async () => {
|
|
try {const response=await fetch('/api/session'); token=(await response.json()).token; await poll();}
|
|
catch(error) {$('notice').textContent='初始化失败,请刷新页面。';}
|
|
})();
|