window.Naut.registerPlugin({
id: 'health',
name: 'Health Overview',
views: [{
id: 'health',
label: 'Health',
render(host, { state, f }) {
const torrents = state.snapshot.torrents;
const lowAvailability = torrents.filter((t) => t.availability < 1.05);
const idleComplete = torrents.filter((t) => t.progress >= 1 && t.upspeed === 0);
const slowDownloads = torrents.filter((t) => t.progress < 1 && t.dlspeed < 32 * 1024);
const rows = [...lowAvailability, ...slowDownloads]
.filter((t, i, arr) => arr.findIndex((x) => x.hash === t.hash) === i)
.sort((a, b) => a.availability - b.availability)
.slice(0, 18);
host.innerHTML = `
${metric('Low availability', lowAvailability.length)}
${metric('Idle completed', idleComplete.length)}
${metric('Slow downloads', slowDownloads.length)}
Torrents needing attention
${rows.length ? table(rows, f) : '
No health issues in the current snapshot
'}
`;
},
}],
sidebarSections: [{
id: 'health',
title: 'Health',
render({ state }) {
const torrents = state.snapshot.torrents;
const attention = torrents.filter((t) => t.availability < 1.05 || (t.progress < 1 && t.dlspeed < 32 * 1024)).length;
return `
+Needs attention${attention}
`;
},
onMount(root, { setView }) {
root.querySelector('[data-health-open]')?.addEventListener('click', () => setView('health'));
},
}],
detailTabs: [{
id: 'health-detail',
label: 'Health',
async render(host, { api, hash, f }) {
const t = await api.torrent(hash);
host.innerHTML = `
Health
${prop('Availability', t.availability.toFixed(3))}
${prop('Connected seeds', `${t.seeds} / ${t.seedsTotal}`)}
${prop('Connected peers', `${t.peers} / ${t.peersTotal}`)}
${prop('Last activity', f.ago(t.lastActivity))}
${prop('Transfer state', t.dlspeed || t.upspeed ? 'active' : 'idle')}
`;
},
}],
detailPanels: [{
id: 'health-summary',
title: 'Plugin: Health',
render(t, { f }) {
const flags = [];
if (t.availability < 1.05) flags.push('low availability');
if (t.progress < 1 && t.dlspeed < 32 * 1024) flags.push('slow download');
if (t.progress >= 1 && t.upspeed === 0) flags.push('idle seeding');
return `${prop('Attention', flags.length ? flags.join(', ') : 'none')}
${prop('Last activity', f.ago(t.lastActivity))}`;
},
}],
});
function metric(label, value) {
return ``;
}
function prop(k, v) {
return `${k}${v}
`;
}
function table(rows, f) {
return `
| Name | Done | Availability | Down | Up |
${rows.map((t) => `
| ${f.esc(t.name)} |
${f.pct(t.progress)} |
${t.availability.toFixed(2)} |
${f.rate(t.dlspeed)} |
${f.rate(t.upspeed)} |
`).join('')}
`;
}