Record an episode that turns out to be a torrent, and run it off the worker (#155)
An enclosure advertised as audio whose body is a .torrent was handed to torrent_one from inside fetch_one, which returned without mark_downloaded, and in a scan nothing else recorded it. It stayed pending: the page never showed it downloaded, and every scan of its feed fetched the .torrent again and ran the torrent again, on the worker, for up to the stall limit and then the seeding time (30 and 60 minutes in production), with no other feed checked meanwhile. It now goes the way an advertised torrent does: in the daemon, marked downloading and handed to spawn_torrent, which records it when it finishes; in a one-off CLI run, run there and recorded. fetch_one answers None for the first, which a scan counts as a torrent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Fixed
|
||||
|
||||
- An episode whose file turns out to be a torrent shows as downloaded once it is, instead of being fetched again on every check of its feed, and no longer holds up the checking of other feeds while it downloads and seeds.
|
||||
- Downloading the latest 65 to 100 items of a feed queues them at once, instead of hanging until most had downloaded and holding up adding feeds and Refresh meanwhile.
|
||||
- Changes to the feed list made at the same moment, such as two people adding feeds, or a feed added while the server settings change, no longer undo one another.
|
||||
- A download that stops partway is given up after two minutes without data, instead of holding up every scan and download until a restart.
|
||||
|
||||
26
src/main.rs
26
src/main.rs
@@ -1761,7 +1761,8 @@ async fn scan_one(
|
||||
continue;
|
||||
}
|
||||
match fetch_one(ctx, id, item.id, feed_cfg, &item.url, &dest_dir).await {
|
||||
Ok((path, bytes)) => {
|
||||
Ok(None) => scan.torrents += 1,
|
||||
Ok(Some((path, bytes))) => {
|
||||
ctx.out.emit(Event::DownloadDone {
|
||||
feed: id.to_string(),
|
||||
enclosure: item.id,
|
||||
@@ -1996,6 +1997,8 @@ fn merge_policy(subs: &[db::Sub], feed_cfg: &config::Feed, global: usize) -> Pol
|
||||
policy
|
||||
}
|
||||
|
||||
/// Downloads one file and records it. None when it turned out to be a torrent, handed off to run
|
||||
/// on its own, which records it when it finishes.
|
||||
#[tracing::instrument(name = "download", skip_all, fields(feed = feed_id, enclosure = enclosure, url = url))]
|
||||
async fn fetch_one(
|
||||
ctx: &Arc<Ctx>,
|
||||
@@ -2004,7 +2007,7 @@ async fn fetch_one(
|
||||
feed_cfg: &config::Feed,
|
||||
url: &str,
|
||||
dest_dir: &std::path::Path,
|
||||
) -> Result<(PathBuf, u64)> {
|
||||
) -> Result<Option<(PathBuf, u64)>> {
|
||||
// Throttled to whole percents, as the original's lastDLStepSize guard did.
|
||||
let mut last_pct = -1i64;
|
||||
let name = download::filename_for(url, None);
|
||||
@@ -2035,12 +2038,22 @@ async fn fetch_one(
|
||||
ctx.db.mark_enclosure(url, "skipped", Some("torrents disabled")).await?;
|
||||
anyhow::bail!("body is a torrent and torrents are disabled");
|
||||
}
|
||||
return torrent_one(ctx, feed_id, enclosure, url, dest_dir).await;
|
||||
// As one advertised as a torrent goes: off the worker where it can be, and recorded either
|
||||
// way. Run here and never recorded, it stayed pending, and every scan of its feed ran it
|
||||
// again, on the worker, for up to the stall limit and the seeding time (#155).
|
||||
if ctx.detach_torrents {
|
||||
ctx.db.mark_enclosure(url, "downloading", None).await?;
|
||||
spawn_torrent(ctx, feed_id.to_owned(), enclosure, url.to_owned(), dest_dir.to_owned());
|
||||
return Ok(None);
|
||||
}
|
||||
let (path, bytes) = torrent_one(ctx, feed_id, enclosure, url, dest_dir).await?;
|
||||
ctx.db.mark_downloaded(url, &path, bytes).await?;
|
||||
return Ok(Some((path, bytes)));
|
||||
}
|
||||
|
||||
let path = download::place(&got, dest_dir).await?;
|
||||
ctx.db.mark_downloaded(url, &path, got.bytes).await?;
|
||||
Ok((path, got.bytes))
|
||||
Ok(Some((path, got.bytes)))
|
||||
}
|
||||
|
||||
/// Runs a torrent off the command worker, so feed scans and HTTP downloads keep moving
|
||||
@@ -2114,14 +2127,15 @@ async fn download_one(ctx: &Arc<Ctx>, id: i64) -> Result<()> {
|
||||
if !cfg.torrent.enabled {
|
||||
Err(anyhow::anyhow!("torrents are disabled"))
|
||||
} else {
|
||||
torrent_one(ctx, &enc.feed_id, enc.id, &enc.url, &dest_dir).await
|
||||
torrent_one(ctx, &enc.feed_id, enc.id, &enc.url, &dest_dir).await.map(Some)
|
||||
}
|
||||
} else {
|
||||
fetch_one(ctx, &enc.feed_id, enc.id, feed_cfg, &enc.url, &dest_dir).await
|
||||
};
|
||||
|
||||
match result {
|
||||
Ok((path, bytes)) => {
|
||||
Ok(None) => {} // A torrent after all, running on its own.
|
||||
Ok(Some((path, bytes))) => {
|
||||
ctx.db.mark_downloaded(&enc.url, &path, bytes).await?;
|
||||
ctx.out.emit(Event::DownloadDone {
|
||||
feed: enc.feed_id.clone(),
|
||||
|
||||
Reference in New Issue
Block a user