Keep OPML feeds out of config, cap downloads, log daemon work

Writing 82 derived feeds into a hand-edited config.toml made it
unreadable. The OPML is the source of truth, so its feeds are re-derived
each scan and held in the database, inheriting the subscription's
settings; editing one promotes it to a real entry. A migration moves
existing children out -- 611 lines to 38 -- keeping all entries and files.

max_new_per_check defaulted to unlimited, so subscribing to an OPML of 82
feeds pulled whole back catalogues. It now defaults to 3 via [general],
capping every feed that does not set its own, and the pending queue orders
by publish date so a cap of 3 means the three newest.

Scans and downloads travelled as socket events only, so the log view
showed no daemon activity. They are mirrored into tracing, with routine
skips at debug -- at 82 feeds those alone would flush the buffer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AdXho5tTkjFLeUXKbEjKBh
This commit is contained in:
2026-09-10 15:15:54 +00:00
parent c86d698363
commit 665d5b8ecb
9 changed files with 456 additions and 77 deletions

View File

@@ -34,6 +34,10 @@ pub struct General {
pub max_total_gb: f64,
/// 0 = keep forever.
pub max_age_days: u64,
/// How many new enclosures a single scan may take, when a feed does not say.
/// Unlimited by default was a trap: subscribing to an OPML of 80 feeds then pulled
/// every back-catalogue episode at once. 0 means unlimited, deliberately chosen.
pub max_new_per_check: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
@@ -104,7 +108,7 @@ pub struct Feed {
/// Overrides the global schedule for this feed. Same forms: "every 6h", "2d".
#[serde(default, skip_serializing_if = "Option::is_none")]
pub schedule: Option<String>,
/// Cap on new downloads per scan. None = unlimited.
/// Cap on new downloads per scan for this feed. None follows `[general]`.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_new_per_check: Option<usize>,
#[serde(default, skip_serializing_if = "Option::is_none")]
@@ -130,6 +134,7 @@ impl Default for General {
organize: Organize::Feed,
max_total_gb: 0.0,
max_age_days: 0,
max_new_per_check: 3,
}
}
}