diff --git a/public/js/api.js b/public/js/api.js index 101c410..5e3cc59 100644 --- a/public/js/api.js +++ b/public/js/api.js @@ -47,6 +47,8 @@ export const api = { deleteFeed: (name) => jpost('/api/rss/delete', { name }), saveRssRule: (rule) => jpost('/api/rss/rules', rule), deleteRssRule: (name) => jpost('/api/rss/rules/delete', { name }), + runRssRule: (name) => jpost('/api/rss/rules/run', { name }), + refreshFeeds: (name) => jpost('/api/rss/refresh', name ? { name } : {}), rssDownload: (item) => jpost('/api/rss/download', item), saveIndexer: (ix) => jpost('/api/indexers', ix), deleteIndexer: (name) => jpost('/api/indexers/delete', { name }), diff --git a/public/js/app.js b/public/js/app.js index d35b826..993c113 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -962,10 +962,13 @@ async function renderRssView(host, seq) { host.innerHTML = `
-

Feeds

+

Feeds

+ +
${feeds.length ? feeds.map((fd) => `
📡 ${f.esc(fd.name)} (${fd.articles.length}) ${fd.lastUpdate ? f.ago(fd.lastUpdate) : 'not fetched yet'} +
${f.esc(fd.url)}
`).join('
') : '
No feeds yet. Add one to start polling.
'} @@ -977,7 +980,9 @@ async function renderRssView(host, seq) { ${articles.map((a, i) => ` ${f.esc(a.feed)}${f.esc(a.title)} ${a.size ? f.bytes(a.size) : '—'}${f.esc(a.pubDate || '')} - ${(a.magnet || a.torrentUrl) ? `` : ''}`).join('')} + ${(a.magnet || a.torrentUrl) + ? `` + : ''}`).join('')} ` : '
No articles yet.
'}

Auto-download rules

@@ -987,6 +992,17 @@ async function renderRssView(host, seq) { document.getElementById('addFeedBtn').addEventListener('click', openAddFeed); document.getElementById('addRuleBtn').addEventListener('click', () => openRuleEditor(null, feeds)); + document.getElementById('refreshAllBtn').addEventListener('click', (e) => guard(async () => { + e.target.disabled = true; e.target.textContent = '⟳ Refreshing…'; + await api.refreshFeeds(); + renderView(); + }, 'Failed to refresh feeds')); + host.querySelectorAll('.repull-feed').forEach((b) => b.addEventListener('click', () => guard(async () => { + const name = b.closest('.feed-row').dataset.feed; + b.disabled = true; + await api.refreshFeeds(name); + renderView(); + }, 'Failed to refresh feed'))); host.querySelectorAll('.del-feed').forEach((b) => b.addEventListener('click', async () => { const name = b.closest('.feed-row').dataset.feed; await guard(() => api.deleteFeed(name), 'Failed to remove feed'); @@ -994,11 +1010,16 @@ async function renderRssView(host, seq) { })); host.querySelectorAll('.dl-art').forEach((b) => b.addEventListener('click', () => guard(async () => { const a = articles[+b.dataset.art]; - await api.rssDownload({ magnet: a.magnet || '', torrentUrl: a.torrentUrl || '' }); + await api.rssDownload({ magnet: a.magnet || '', torrentUrl: a.torrentUrl || '', key: a.key || '' }); b.textContent = '✓ Added'; b.disabled = true; }, 'Failed to add torrent'))); host.querySelectorAll('[data-rule-edit]').forEach((b) => b.addEventListener('click', () => openRuleEditor(rules.find((r) => r.name === b.dataset.ruleEdit), feeds))); + host.querySelectorAll('[data-rule-run]').forEach((b) => b.addEventListener('click', () => guard(async () => { + const res = await api.runRssRule(b.dataset.ruleRun); + toast(`Rule run: ${res.grabbed} added of ${res.matched} match(es)`, 'ok'); + renderView(); + }, 'Failed to run rule'))); host.querySelectorAll('[data-rule-del]').forEach((b) => b.addEventListener('click', async () => { await guard(() => api.deleteRssRule(b.dataset.ruleDel), 'Failed to delete rule'); renderView(); @@ -1074,6 +1095,7 @@ function renderRule(r) { ${r.useRegex ? 'regex' : ''} ${r.addPaused ? 'add paused' : ''} +