forked from jsl/video_editing_poc
• Implemented the next Input Folder Scheduler Plan milestone: startup directory initialization.
Added src/main/java/org/example/videoclips/folder/FolderSchedulerDirectoryInitializer.java, which creates the configured input, output, working, processed, and rejected directories when video-clipping.folder- scheduler.enabled=true. It stays inactive by default, so normal startup will not fail on machines where /input and /output cannot be created. Updated docs/input-folder-scheduler-plan.md to mark milestone 3 complete.
This commit is contained in:
parent
1cda571c30
commit
97cc4ff4e4
|
|
@ -19,8 +19,8 @@ Note: the local machine's filesystem root is read-only in this workspace, so abs
|
||||||
## Milestones
|
## Milestones
|
||||||
|
|
||||||
1. [x] Create repository-local placeholder directories `input/source` and `output/clips`.
|
1. [x] Create repository-local placeholder directories `input/source` and `output/clips`.
|
||||||
2. [ ] Add folder scheduler configuration properties.
|
2. [x] Add folder scheduler configuration properties.
|
||||||
3. [ ] Create startup directory initialization for input, output, working, processed, and rejected folders.
|
3. [x] Create startup directory initialization for input, output, working, processed, and rejected folders.
|
||||||
4. [ ] Add deterministic scanner that picks one candidate video at a time.
|
4. [ ] Add deterministic scanner that picks one candidate video at a time.
|
||||||
5. [ ] Add repeat-prevention by moving selected inputs to a working folder before processing.
|
5. [ ] Add repeat-prevention by moving selected inputs to a working folder before processing.
|
||||||
6. [ ] Add valid-video detection using extension filtering and `ffprobe`.
|
6. [ ] Add valid-video detection using extension filtering and `ffprobe`.
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,8 @@ public class VideoClippingProperties {
|
||||||
|
|
||||||
private final Ffmpeg ffmpeg = new Ffmpeg();
|
private final Ffmpeg ffmpeg = new Ffmpeg();
|
||||||
|
|
||||||
|
private final FolderScheduler folderScheduler = new FolderScheduler();
|
||||||
|
|
||||||
private final Cleanup cleanup = new Cleanup();
|
private final Cleanup cleanup = new Cleanup();
|
||||||
|
|
||||||
private final Quotas quotas = new Quotas();
|
private final Quotas quotas = new Quotas();
|
||||||
|
|
@ -116,6 +118,10 @@ public class VideoClippingProperties {
|
||||||
return ffmpeg;
|
return ffmpeg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FolderScheduler getFolderScheduler() {
|
||||||
|
return folderScheduler;
|
||||||
|
}
|
||||||
|
|
||||||
public Cleanup getCleanup() {
|
public Cleanup getCleanup() {
|
||||||
return cleanup;
|
return cleanup;
|
||||||
}
|
}
|
||||||
|
|
@ -335,6 +341,130 @@ public class VideoClippingProperties {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class FolderScheduler {
|
||||||
|
private boolean enabled = false;
|
||||||
|
|
||||||
|
private String inputDirectory = "/input/source";
|
||||||
|
|
||||||
|
private String outputDirectory = "/output/clips";
|
||||||
|
|
||||||
|
private String processedDirectory = "/input/processed";
|
||||||
|
|
||||||
|
private String rejectedDirectory = "/input/rejected";
|
||||||
|
|
||||||
|
private String workingDirectory = "/input/working";
|
||||||
|
|
||||||
|
@Min(1000)
|
||||||
|
private long pollIntervalMs = 5000;
|
||||||
|
|
||||||
|
@Min(1)
|
||||||
|
private int segmentDurationSeconds = 8;
|
||||||
|
|
||||||
|
private String ffmpegBinary = "ffmpeg";
|
||||||
|
|
||||||
|
private String ffprobeBinary = "ffprobe";
|
||||||
|
|
||||||
|
private String outputContainer = "mp4";
|
||||||
|
|
||||||
|
private String exactPreset = "veryfast";
|
||||||
|
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(boolean enabled) {
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getInputDirectory() {
|
||||||
|
return inputDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInputDirectory(String inputDirectory) {
|
||||||
|
this.inputDirectory = inputDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOutputDirectory() {
|
||||||
|
return outputDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOutputDirectory(String outputDirectory) {
|
||||||
|
this.outputDirectory = outputDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProcessedDirectory() {
|
||||||
|
return processedDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProcessedDirectory(String processedDirectory) {
|
||||||
|
this.processedDirectory = processedDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRejectedDirectory() {
|
||||||
|
return rejectedDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRejectedDirectory(String rejectedDirectory) {
|
||||||
|
this.rejectedDirectory = rejectedDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getWorkingDirectory() {
|
||||||
|
return workingDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWorkingDirectory(String workingDirectory) {
|
||||||
|
this.workingDirectory = workingDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getPollIntervalMs() {
|
||||||
|
return pollIntervalMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPollIntervalMs(long pollIntervalMs) {
|
||||||
|
this.pollIntervalMs = pollIntervalMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSegmentDurationSeconds() {
|
||||||
|
return segmentDurationSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSegmentDurationSeconds(int segmentDurationSeconds) {
|
||||||
|
this.segmentDurationSeconds = segmentDurationSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFfmpegBinary() {
|
||||||
|
return ffmpegBinary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFfmpegBinary(String ffmpegBinary) {
|
||||||
|
this.ffmpegBinary = ffmpegBinary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFfprobeBinary() {
|
||||||
|
return ffprobeBinary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFfprobeBinary(String ffprobeBinary) {
|
||||||
|
this.ffprobeBinary = ffprobeBinary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOutputContainer() {
|
||||||
|
return outputContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOutputContainer(String outputContainer) {
|
||||||
|
this.outputContainer = outputContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getExactPreset() {
|
||||||
|
return exactPreset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExactPreset(String exactPreset) {
|
||||||
|
this.exactPreset = exactPreset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static class Quotas {
|
public static class Quotas {
|
||||||
@Min(0)
|
@Min(0)
|
||||||
private int maxActiveJobsPerTenant = 5;
|
private int maxActiveJobsPerTenant = 5;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
package org.example.videoclips.folder;
|
||||||
|
|
||||||
|
import org.example.videoclips.config.VideoClippingProperties;
|
||||||
|
import org.springframework.boot.ApplicationArguments;
|
||||||
|
import org.springframework.boot.ApplicationRunner;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@ConditionalOnProperty(name = "video-clipping.folder-scheduler.enabled", havingValue = "true")
|
||||||
|
public class FolderSchedulerDirectoryInitializer implements ApplicationRunner {
|
||||||
|
|
||||||
|
private final VideoClippingProperties.FolderScheduler properties;
|
||||||
|
|
||||||
|
public FolderSchedulerDirectoryInitializer(VideoClippingProperties videoClippingProperties) {
|
||||||
|
this.properties = videoClippingProperties.getFolderScheduler();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(ApplicationArguments args) {
|
||||||
|
List.of(
|
||||||
|
properties.getInputDirectory(),
|
||||||
|
properties.getOutputDirectory(),
|
||||||
|
properties.getWorkingDirectory(),
|
||||||
|
properties.getProcessedDirectory(),
|
||||||
|
properties.getRejectedDirectory()
|
||||||
|
).forEach(this::createDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createDirectory(String directory) {
|
||||||
|
try {
|
||||||
|
Files.createDirectories(Path.of(directory));
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new IllegalStateException("Unable to create folder scheduler directory: " + directory, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -25,6 +25,18 @@ video-clipping.ffmpeg.ffmpeg-binary=ffmpeg
|
||||||
video-clipping.ffmpeg.input-directory=./tmp/ffmpeg-input
|
video-clipping.ffmpeg.input-directory=./tmp/ffmpeg-input
|
||||||
video-clipping.ffmpeg.output-directory=./tmp/ffmpeg-output
|
video-clipping.ffmpeg.output-directory=./tmp/ffmpeg-output
|
||||||
video-clipping.ffmpeg.cleanup-local-files=true
|
video-clipping.ffmpeg.cleanup-local-files=true
|
||||||
|
video-clipping.folder-scheduler.enabled=false
|
||||||
|
video-clipping.folder-scheduler.input-directory=/input/source
|
||||||
|
video-clipping.folder-scheduler.output-directory=/output/clips
|
||||||
|
video-clipping.folder-scheduler.processed-directory=/input/processed
|
||||||
|
video-clipping.folder-scheduler.rejected-directory=/input/rejected
|
||||||
|
video-clipping.folder-scheduler.working-directory=/input/working
|
||||||
|
video-clipping.folder-scheduler.poll-interval-ms=5000
|
||||||
|
video-clipping.folder-scheduler.segment-duration-seconds=8
|
||||||
|
video-clipping.folder-scheduler.ffmpeg-binary=ffmpeg
|
||||||
|
video-clipping.folder-scheduler.ffprobe-binary=ffprobe
|
||||||
|
video-clipping.folder-scheduler.output-container=mp4
|
||||||
|
video-clipping.folder-scheduler.exact-preset=veryfast
|
||||||
video-clipping.quotas.max-active-jobs-per-tenant=5
|
video-clipping.quotas.max-active-jobs-per-tenant=5
|
||||||
video-clipping.cleanup.enabled=true
|
video-clipping.cleanup.enabled=true
|
||||||
video-clipping.cleanup.local-artifact-poll-interval-ms=300000
|
video-clipping.cleanup.local-artifact-poll-interval-ms=300000
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue