// Thin API client + SSE live stream. async function jget(url) { const r = await fetch(url); if (!r.ok) throw new Error(`${url} → ${r.status}`); return r.json(); } async function jpost(url, body) { const r = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body || {}), }); if (!r.ok) throw new Error(`${url} → ${r.status}`); return r.json(); } export const api = { authStatus: () => jget('/api/auth/status'), login: (username, password) => jpost('/api/login', { username, password }), logout: () => jpost('/api/logout', {}), plugins: () => jget('/api/plugins'), meta: () => jget('/api/meta'), snapshot: () => jget('/api/snapshot'), torrent: (hash) => jget(`/api/torrents/${hash}`), trackers: (hash) => jget(`/api/torrents/${hash}/trackers`), peers: (hash) => jget(`/api/torrents/${hash}/peers`), files: (hash) => jget(`/api/torrents/${hash}/files`), pieces: (hash) => jget(`/api/torrents/${hash}/pieces`), action: (action, hashes, params) => jpost('/api/action', { action, hashes, params }), remove: (hashes, deleteFiles) => jpost('/api/delete', { hashes, deleteFiles }), add: (opts) => jpost('/api/add', opts), toggleAltSpeed: () => jpost('/api/altspeed', {}), createCategory: (name, savePath) => jpost('/api/categories', { name, savePath }), deleteCategory: (name) => jpost('/api/categories/delete', { name }), editCategory: (name, newName, savePath) => jpost('/api/categories/edit', { name, newName, savePath }), createTag: (name) => jpost('/api/tags', { name }), deleteTag: (name) => jpost('/api/tags/delete', { name }), rss: () => jget('/api/rss'), rssRules: () => jget('/api/rss/rules'), addFeed: (name, url) => jpost('/api/rss', { name, url }), deleteFeed: (name) => jpost('/api/rss/delete', { name }), saveRssRule: (rule) => jpost('/api/rss/rules', rule), deleteRssRule: (name) => jpost('/api/rss/rules/delete', { name }), rssDownload: (item) => jpost('/api/rss/download', item), saveIndexer: (ix) => jpost('/api/indexers', ix), deleteIndexer: (name) => jpost('/api/indexers/delete', { name }), script: () => jget('/api/script'), saveScript: (source) => jpost('/api/script', { source }), saveScriptSettings: (settings) => jpost('/api/script/settings', { settings }), search: (q) => jget(`/api/search?q=${encodeURIComponent(q)}`), // Live snapshot stream. onSnapshot(snapshot) called ~1/s. stream(onSnapshot, onStatus) { const es = new EventSource('/api/stream'); es.addEventListener('snapshot', (e) => onSnapshot(JSON.parse(e.data))); es.onopen = () => onStatus && onStatus('connected'); es.onerror = () => onStatus && onStatus('reconnecting'); return es; }, };