10 KiB
Input Folder Scheduler Plan
Goal
Add a scheduler that scans an input folder for video files and creates 8-second clips into an output folder.
Requested runtime paths:
- input:
/input/source - output:
/output/clips
Repository-local placeholder directories:
input/sourceoutput/clips
Note: the local machine's filesystem root is read-only in this workspace, so absolute /input/source and /output/clips could not be created here. In production, mount or create those absolute paths and configure the service to use them.
Milestones
- Create repository-local placeholder directories
input/sourceandoutput/clips. - Add folder scheduler configuration properties.
- Create startup directory initialization for input, output, working, processed, and rejected folders.
- Add deterministic scanner that picks one candidate video at a time.
- Add repeat-prevention by moving selected inputs to a working folder before processing.
- Add valid-video detection using extension filtering and
ffprobe. - Add invalid-file handling that logs the reason and moves files to rejected storage.
- Add FFmpeg clipping into
/output/clips/<video-name>/using 8-second MP4/H.264/AAC segments. - Add success handling that moves processed source files to processed storage.
- Add failure handling for FFmpeg or filesystem errors.
- Add unit tests for ordering, invalid-file handling, repeat prevention, and state transitions.
- Add optional FFmpeg integration test for a short fixture.
- Add local profile or documented runtime config for enabling the scheduler.
- Run verification and mark feature complete.
Format Recommendation
Use .mp4 clips containing:
- video: H.264 / AVC via
libx264 - audio: AAC via
aac - MIME type:
video/mp4
Rationale:
- MDN documents MP4 as a popular, broadly supported container.
- MDN documents MPEG-4 / MP4 as supported by all major browsers and lists H.264 and AAC support for MP4.
- MDN specifically recommends MP4 with H.264 video and AAC audio as a broadly supported combination across every major browser.
- FFmpeg's segment muxer supports
segment_time; its documentation notes that accurate splitting needs keyframes at the split points, so exact 8-second clips should force keyframes or transcode.
Sources:
- https://developer.mozilla.org/en-US/docs/Web/Media/Guides/Formats/Containers
- https://developer.mozilla.org/en-US/docs/Web/Media/Guides/Formats/Video_codecs
- https://ffmpeg.org/ffmpeg-formats.html#segment_002c-stream_005fsegment_002c-ssegment
Configuration
Add a new scheduler config group under video-clipping.folder-scheduler.
Recommended properties:
video-clipping.folder-scheduler.enabled=true
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
Keep the existing API-driven clipper config separate from this folder scheduler. The folder scheduler is a direct filesystem workflow, not an API/upload/session workflow.
Processing Rules
The scheduler should:
- Create all configured directories on startup if they do not exist.
- Scan
input-directoryon a fixed delay. - Ignore directories, hidden files, temporary files, and files currently being written.
- Pick only one candidate at a time.
- Process candidates in deterministic order, preferably filename natural order so
1.mp4is processed before2.mp4. - Validate a candidate with
ffprobe, not only by file extension. - Move invalid files to
rejected-directoryand log why they were rejected. - Move a selected valid input to
working-directorybefore processing, so the same file is not picked again while processing. - Write clips to
output-directory/<video-name>/. - Move the source file to
processed-directoryonly after all clips are successfully created. - If processing fails, move the source file to
rejected-directoryor leave it inworking-directorywith a failure marker, depending on operator preference.
Use the source filename stem for the output directory:
/input/source/1.mp4 -> /output/clips/1/clip_00000.mp4
/input/source/1.mp4 -> /output/clips/1/clip_00001.mp4
/input/source/1.mp4 -> /output/clips/1/clip_00002.mp4
If two input files share the same stem, append a stable suffix:
/output/clips/1
/output/clips/1-20260710T141500Z
Valid Video Detection
Use a two-stage validation:
- Extension allowlist for cheap filtering:
.mp4.mov.m4v.webm.mkv
ffprobeverification for real validation.
Suggested ffprobe command:
ffprobe -v error \
-select_streams v:0 \
-show_entries stream=codec_type,codec_name,width,height \
-show_entries format=duration \
-of json \
/input/working/1.mp4
Accept the file only if:
ffprobeexits with code0- at least one video stream exists
- duration is present and greater than
0 - width and height are positive
Log invalid files with:
- source path
- reason
- ffprobe exit code
- sanitized ffprobe output
Do not log raw paths from untrusted metadata or full FFmpeg stderr if it contains sensitive filesystem details.
FFmpeg Command
Default to exact 8-second MP4/H.264/AAC output:
ffmpeg -hide_banner -y \
-i /input/working/1.mp4 \
-map 0 \
-c:v libx264 \
-preset veryfast \
-crf 20 \
-force_key_frames "expr:gte(t,n_forced*8)" \
-c:a aac \
-b:a 128k \
-f segment \
-segment_time 8 \
-segment_format_options movflags=+faststart \
-reset_timestamps 1 \
/output/clips/1/clip_%05d.mp4
Expected behavior:
- each generated clip is approximately 8 seconds
- the final clip may be shorter than 8 seconds
- output files are broadly playable MP4 files
Implementation note:
- If exact boundaries are more important than speed, always transcode with
libx264and force keyframes. - If speed is more important and keyframe-aligned cuts are acceptable, add an optional
FASTmode using-c copy, but document that clips may not start exactly at 8-second boundaries.
Proposed Code Structure
Add:
src/main/java/org/example/videoclips/folder/FolderSchedulerProperties.javasrc/main/java/org/example/videoclips/folder/FolderVideoScanScheduler.javasrc/main/java/org/example/videoclips/folder/FolderVideoValidator.javasrc/main/java/org/example/videoclips/folder/FolderFfmpegClipper.javasrc/test/java/org/example/videoclips/folder/FolderVideoScanSchedulerTest.java
Responsibilities:
FolderSchedulerProperties: typed config for directories, binaries, poll interval, segment length, and output format.FolderVideoScanScheduler: scheduled scanner, single-file lock, ordering, state moves.FolderVideoValidator: extension filtering andffprobevalidation.FolderFfmpegClipper: FFmpeg command construction and process execution.- Tests: validate ordering, invalid-file rejection, no repeat processing, and command construction.
Concurrency Control
Use an in-process guard to prevent overlapping scheduled runs:
AtomicBoolean processing- or
ReentrantLock tryLock()
Only one file should process at a time. This directly satisfies:
1.mp4 completes before 2.mp4 starts
If the service will run multiple replicas against the same mounted folder, add a filesystem lock or database lease before enabling more than one scheduler instance.
File State Flow
Recommended state transitions:
/input/source/1.mp4
-> /input/working/1.mp4
-> /output/clips/1/clip_00000.mp4
-> /output/clips/1/clip_00001.mp4
-> /input/processed/1.mp4
Invalid file:
/input/source/not-video.txt
-> /input/rejected/not-video.txt
Failed processing:
/input/working/broken.mp4
-> /input/rejected/broken.mp4
This ensures the scheduler never processes the same source file repeatedly.
Operational Logging
Log these events:
- scheduler started and configured directories
- no candidate found at debug level
- selected candidate
- invalid candidate rejected
- FFmpeg command started, without sensitive metadata
- clip generation succeeded with clip count
- source moved to processed directory
- processing failed with sanitized error
Testing Plan
Unit tests:
- creates configured folders
- sorts
1.mp4before2.mp4 - ignores hidden and temporary files
- rejects invalid extension before
ffprobe - rejects
ffprobefailure - moves valid input to working before processing
- moves successful source to processed
- does not pick the same input twice
Integration tests:
- run FFmpeg against a short generated fixture if
ffmpegandffprobeare installed - verify a 17-second input produces 3 clips
- verify output path is
output/clips/<video-name>/clip_%05d.mp4
Rollout Plan
- Add config properties and disabled-by-default scheduler.
- Add directory creation and scanning behavior.
- Add validation and rejection flow.
- Add FFmpeg clipping flow.
- Add unit tests for file-state transitions.
- Add optional integration test gated on local FFmpeg availability.
- Enable in a local profile first.
- Enable in production only with mounted
/inputand/outputvolumes.
Acceptance Criteria
The feature is complete when:
/input/sourceand/output/clipsare configurable and created on startup- valid video files are processed one at a time
1.mp4is processed before2.mp4under deterministic ordering- clips are written to
/output/clips/<video-name>/ - each clip is MP4/H.264/AAC by default
- invalid files are ignored for processing, moved to rejected storage, and logged
- processed source files are not picked up again
- tests cover ordering, invalid file handling, and repeat-prevention behavior