Remove DeadAir copies unplayed for a week, and remake them on play (#179)
A copy is nearly the size of its original and was kept as long as the original. Now `deadair.played_at` notes when one was last served (at most once an hour, since a player asks again at every seek), and each pass of the maker removes copies unplayed, or unmade, for 7 days. Only the file goes: the row keeps its cuts with its path cleared, which tells an expired copy apart from a file that cannot have one (no cuts) and keeps the background queue from making it again straight away. Serving the original of such an episode deletes the row, which puts it back on the queue; the copy then comes back within a pass, though the page does not switch to it mid-play (#178). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -37,6 +37,9 @@ const MAX_CUTS: usize = 3500;
|
||||
/// How long the maker waits when there is nothing to do: a download or a feed turned on is
|
||||
/// picked up this long after, at most.
|
||||
const IDLE: std::time::Duration = std::time::Duration::from_secs(120);
|
||||
/// How long a copy is kept after it was last played, or made if nobody has played it (#179). It
|
||||
/// takes nearly as much space as the original, and playing the original makes it again.
|
||||
const KEEP_COPY: i64 = 7 * 24 * 3600;
|
||||
|
||||
/// A span of the original, in milliseconds, that the copy leaves out.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
@@ -248,6 +251,9 @@ pub async fn run(ctx: Arc<Ctx>, mut stop: tokio::sync::watch::Receiver<bool>) {
|
||||
// as the file having no copy was never tried again, even once a fix was deployed (#174).
|
||||
let mut failed = std::collections::HashSet::new();
|
||||
loop {
|
||||
if let Err(e) = expire(&ctx).await {
|
||||
tracing::warn!(error = %format!("{e:#}"), "DeadAir could not remove old copies");
|
||||
}
|
||||
// Dropping the pass on a signal kills the ffmpeg it is running (`kill_on_drop`).
|
||||
let made = tokio::select! {
|
||||
_ = stop.changed() => return,
|
||||
@@ -266,6 +272,28 @@ pub async fn run(ctx: Arc<Ctx>, mut stop: tokio::sync::watch::Receiver<bool>) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes the copies nobody has played for `KEEP_COPY`. Their cuts stay, and playing the
|
||||
/// original puts the episode back on the list (`Db::revive_copy`).
|
||||
pub async fn expire(ctx: &Ctx) -> Result<()> {
|
||||
for (id, path) in ctx.db.stale_copies(crate::db::now() - KEEP_COPY).await? {
|
||||
// Only ever DeadAir's own file (`copy_path`): anything else would be someone's original.
|
||||
if !path.contains(".deadair") {
|
||||
continue;
|
||||
}
|
||||
match std::fs::remove_file(&path) {
|
||||
Ok(()) => {}
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {}
|
||||
Err(e) => {
|
||||
tracing::warn!(path, error = %e, "DeadAir could not remove an old copy");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ctx.db.drop_copy_path(id).await?;
|
||||
tracing::info!(path, "DeadAir copy removed, unplayed for a week");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn have_ffmpeg() -> bool {
|
||||
tokio::process::Command::new("ffmpeg")
|
||||
.arg("-version")
|
||||
|
||||
Reference in New Issue
Block a user