diff --git a/docs/production-cinematic-highlight-editing-plan.md b/docs/production-cinematic-highlight-editing-plan.md index 03ff095..9ea281a 100644 --- a/docs/production-cinematic-highlight-editing-plan.md +++ b/docs/production-cinematic-highlight-editing-plan.md @@ -313,7 +313,8 @@ The plan should be strict JSON so a cheaper model or deterministic renderer can - [x] Milestone 14 progress: Renderer now writes `qa-report.json` with structural checks, asset resolution checks, overlay safety checks, audio mastering checks, FFmpeg command completion checks, and FFmpeg probe checks for black frames, long silence, and audio clipping. - [x] Milestone 15: Add an operator runbook for placing a video, running the service locally, choosing a model, reviewing the director plan, and finding final clips. - [x] Milestone 15 progress: Added `docs/cinematic-editing-runbook.md` with local setup, source placement, AI director model guidance, edit-plan review, rendering, final output lookup, QA report review, and troubleshooting. -- [ ] Milestone 16: Add integration tests with small fixture videos for family, food, car, and generic content. +- [x] Milestone 16: Add integration tests with small fixture videos for family, food, car, and generic content. +- [x] Milestone 16 progress: Added FFmpeg-backed local director integration coverage for car, food, family, and generic fixture projects, verifying category artifacts, highlight candidates, and director prompts. - [ ] Milestone 17: Add benchmark metrics for analysis time, render time, token usage, asset generation cost, and final output size. - [ ] Milestone 18: Add a human review mode where the user can approve or edit the AI director plan before rendering. diff --git a/src/test/java/org/example/videoclips/editing/CinematicEditingIntegrationTest.java b/src/test/java/org/example/videoclips/editing/CinematicEditingIntegrationTest.java index dffbb1f..dc30bd6 100644 --- a/src/test/java/org/example/videoclips/editing/CinematicEditingIntegrationTest.java +++ b/src/test/java/org/example/videoclips/editing/CinematicEditingIntegrationTest.java @@ -5,6 +5,8 @@ import org.example.videoclips.config.VideoClippingProperties; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIf; import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import java.io.IOException; import java.nio.file.Files; @@ -65,6 +67,44 @@ class CinematicEditingIntegrationTest { assertThat(projects.getProject(projectId).status()).isEqualTo(EditProjectStatus.RENDERED); } + @ParameterizedTest + @CsvSource({ + "car-drive,car-drive.mp4,CAR_VLOG", + "food-recipe,food-recipe.mp4,FOOD_VLOG", + "family-birthday,family-birthday.mp4,FAMILY_VLOG", + "city-walk,city-walk.mp4,GENERIC_VLOG" + }) + void analyzesCategoryFixturesAndCreatesHighlightCandidates(String projectId, String fileName, + ContentCategory expectedCategory) throws Exception { + VideoClippingProperties properties = properties(); + ObjectMapper mapper = new ObjectMapper().findAndRegisterModules(); + FileSystemEditProjectStore store = new FileSystemEditProjectStore(properties, mapper); + new EditProjectDirectoryInitializer(properties).initialize(); + Path source = Files.createDirectory(tempDir.resolve("source").resolve(projectId)); + createClip(source.resolve(fileName), "green", 550); + + EditProjectService projects = new EditProjectService(store, Clock.systemUTC()); + EditProjectAnalyzer analyzer = new EditProjectAnalyzer(store, new EditClipDiscovery(), + new FfmpegClipInspector(properties, mapper), new ThumbnailExtractor(properties), + new ContactSheetGenerator(properties), new ProxyGenerator(properties), + new CinematicHighlightAnalyzer(Clock.systemUTC()), Clock.systemUTC(), null); + StoryboardPromptGenerator storyboard = new StoryboardPromptGenerator(store); + AiDirectorPromptGenerator director = new AiDirectorPromptGenerator(store, storyboard, properties); + LocalDirectorScheduler scheduler = new LocalDirectorScheduler( + properties, projects, new EditClipDiscovery(), analyzer, director, store); + + scheduler.scan(); + + CinematicHighlightAnalysis category = store.readJson(projectId, "category.json", + CinematicHighlightAnalysis.class); + HighlightCandidate[] candidates = mapper.readValue( + store.projectDirectory(projectId).resolve("highlight-candidates.json").toFile(), + HighlightCandidate[].class); + assertThat(category.category()).isEqualTo(expectedCategory); + assertThat(candidates).isNotEmpty(); + assertThat(store.projectDirectory(projectId).resolve("ai-director-prompt.md")).exists(); + } + private VideoClippingProperties properties() { VideoClippingProperties properties = new VideoClippingProperties(); properties.getEditing().setProjectDirectory(tempDir.resolve("projects").toString());