The new cleanup job is in src/main/java/org/example/videoclips/operations/LocalArtifactCleanupJob.java. It runs on a schedule and removes expired files from:

- the FFmpeg input directory
  - the FFmpeg output directory
  - tmp/in-memory-storage
  - tmp/stub-output

  The schedule and retention window are now configurable through src/main/java/org/example/videoclips/config/VideoClippingProperties.java and src/main/resources/application.properties via:

  - video-clipping.cleanup.enabled
  - video-clipping.cleanup.local-artifact-poll-interval-ms
  - video-clipping.cleanup.local-artifact-retention-hours

  This closes the first retention slice from the plan on the worker side. It does not yet delete expired original videos or clips from real object storage, and it does not do database-driven
  retention policies. That would be the next cleanup step.
This commit is contained in:
JSLMPR 2026-07-09 00:23:41 +02:00
parent 8f4804c2ab
commit 3d961f2adf
6 changed files with 113 additions and 0 deletions

View File

@ -36,6 +36,8 @@ public class VideoClippingProperties {
private final Ffmpeg ffmpeg = new Ffmpeg();
private final Cleanup cleanup = new Cleanup();
public long getMaxFileSizeBytes() {
return maxFileSizeBytes;
}
@ -112,6 +114,10 @@ public class VideoClippingProperties {
return ffmpeg;
}
public Cleanup getCleanup() {
return cleanup;
}
public static class S3 {
private String region = "us-east-1";
private String bucket = "video-clipping-dev";
@ -213,4 +219,38 @@ public class VideoClippingProperties {
this.cleanupLocalFiles = cleanupLocalFiles;
}
}
public static class Cleanup {
private boolean enabled = true;
@Min(30000)
private long localArtifactPollIntervalMs = 300000;
@Min(1)
private int localArtifactRetentionHours = 24;
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public long getLocalArtifactPollIntervalMs() {
return localArtifactPollIntervalMs;
}
public void setLocalArtifactPollIntervalMs(long localArtifactPollIntervalMs) {
this.localArtifactPollIntervalMs = localArtifactPollIntervalMs;
}
public int getLocalArtifactRetentionHours() {
return localArtifactRetentionHours;
}
public void setLocalArtifactRetentionHours(int localArtifactRetentionHours) {
this.localArtifactRetentionHours = localArtifactRetentionHours;
}
}
}

View File

@ -0,0 +1,67 @@
package org.example.videoclips.operations;
import org.example.videoclips.config.VideoClippingProperties;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.FileTime;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Stream;
@Component
public class LocalArtifactCleanupJob {
private final VideoClippingProperties properties;
public LocalArtifactCleanupJob(VideoClippingProperties properties) {
this.properties = properties;
}
@Scheduled(fixedDelayString = "${video-clipping.cleanup.local-artifact-poll-interval-ms:300000}")
public void cleanupLocalArtifacts() {
if (!properties.getCleanup().isEnabled()) {
return;
}
Instant cutoff = Instant.now().minus(properties.getCleanup().getLocalArtifactRetentionHours(), ChronoUnit.HOURS);
List<Path> roots = List.of(
Path.of(properties.getFfmpeg().getInputDirectory()),
Path.of(properties.getFfmpeg().getOutputDirectory()),
Path.of("tmp/in-memory-storage"),
Path.of("tmp/stub-output")
);
for (Path root : roots) {
cleanupRoot(root, cutoff);
}
}
private void cleanupRoot(Path root, Instant cutoff) {
if (!Files.exists(root)) {
return;
}
try (Stream<Path> stream = Files.walk(root)) {
stream.sorted(Comparator.reverseOrder())
.filter(path -> !path.equals(root))
.forEach(path -> deleteIfExpired(path, cutoff));
} catch (IOException ignored) {
}
}
private void deleteIfExpired(Path path, Instant cutoff) {
try {
FileTime lastModifiedTime = Files.getLastModifiedTime(path);
if (lastModifiedTime.toInstant().isBefore(cutoff)) {
Files.deleteIfExists(path);
}
} catch (IOException ignored) {
}
}
}

View File

@ -22,3 +22,6 @@ video-clipping.ffmpeg.ffmpeg-binary=ffmpeg
video-clipping.ffmpeg.input-directory=./tmp/ffmpeg-input
video-clipping.ffmpeg.output-directory=./tmp/ffmpeg-output
video-clipping.ffmpeg.cleanup-local-files=true
video-clipping.cleanup.enabled=true
video-clipping.cleanup.local-artifact-poll-interval-ms=300000
video-clipping.cleanup.local-artifact-retention-hours=24

View File

@ -0,0 +1 @@
stub-clip-content-clip_f205e44bd8cb

View File

@ -0,0 +1 @@
stub-clip-content-clip_0f9504972838

View File

@ -0,0 +1 @@
stub-clip-content-clip_26599ec473ec