Grid compilations: split-screen layouts from random tracks #18

Merged
cheapnud merged 2 commits from f/grid-compilation into master 2026-08-17 10:23:26 +00:00
Owner

The Randomizer gains a layout choice: single, split-screen (2 side by side) or a 2x2 grid. Grid mode generates independent random tracks and places each in its cell with a Size Position Rotate (affine) filter, composited via the same mix/qtblend transitions Shotcut plants - the whole mechanism was verified against melt with a rendered test grid. Audio from all cells mixes together for now.

The Randomizer gains a layout choice: single, split-screen (2 side by side) or a 2x2 grid. Grid mode generates independent random tracks and places each in its cell with a Size Position Rotate (affine) filter, composited via the same mix/qtblend transitions Shotcut plants - the whole mechanism was verified against melt with a rendered test grid. Audio from all cells mixes together for now.
grid compilations: split-screen 2-up or 2x2 layouts built from independent random tracks
All checks were successful
Claude PR Review / AI Code Review (pull_request) Successful in 17s
Build & Test / Build and Test (pull_request) Successful in 29s
0631a7120e

🤖 Claude AI Code Review

  1. Summary
    Adds a grid/split-screen layout option to the Randomizer, generating N independent random tracks each placed via an affine SPR filter and composited with mix/qtblend transitions.

  2. Code Quality
    Clean separation of concerns; the melt-verified compositing logic and fallback defaults are handled thoughtfully.

  3. Issues

🟡 ShotcutService.cs — HasTransition matches any transition with the given mlt_service and any transition with matching b_track, but these two conditions are checked across all transitions independently (via separate .Any() calls on the same collection). This means if a mix transition exists for a different track and a qtblend for this track exists, HasTransition("mix") can return true even though no mix exists for this track. The two Any predicates must be combined into a single t => ... predicate.

bool HasTransition(string mltService) => mainTractor.Transition.Any(t =>
    t.Property.Any(p => p.Name == "mlt_service" && p.Text == mltService) &&
    t.Property.Any(p => p.Name == "b_track" && p.Text == trackIndex.ToString()));

Actually this is combined per-transition correctly — disregard. On re-read the predicate is fine.

🟡 ShotcutService.cs — duration > longestDuration uses playlist.Entry.Sum(e => e.Duration), while SetRenderRangeToPlaylistDuration presumably computes duration independently. If they diverge (e.g. blanks not counted in Entry.Sum), the render range may not match the longest cell. Worth confirming both use the same duration basis.

🟢 Home.razor — _gridLayout uses raw ints (1/2/4) as a magic-value selector; harmless but the value 4 implicitly means "2×2" with no guard if a future option adds 3. GenerateGridCompilation correctly throws on unsupported values, so runtime is safe.

🟢 ShotcutService.cs — Filter Id = $"gridCell{trackIndex}" and transition ids assume trackIndex uniqueness across cells; since each GenerateRandomPlaylist returns a distinct index this holds, but there's no assertion. Minor.

  1. Verdict: APPROVE

The transition-matching logic is correct on re-read, and the remaining points are minor. Before merging, verify that playlist.Entry.Sum(e => e.Duration) and SetRenderRangeToPlaylistDuration compute duration the same way (including blanks), since a mismatch would clip the longest cell — but this is not blocking.


📊 Tokens: 5873 input + 929 output | 💰 Cost: ~$0.0526
Generated by Claude Opus 4.8 via Gitea Actions

## 🤖 Claude AI Code Review 1. **Summary** Adds a grid/split-screen layout option to the Randomizer, generating N independent random tracks each placed via an affine SPR filter and composited with mix/qtblend transitions. 2. **Code Quality** ⭐⭐⭐⭐☆ Clean separation of concerns; the melt-verified compositing logic and fallback defaults are handled thoughtfully. 3. **Issues** 🟡 ShotcutService.cs — `HasTransition` matches any transition with the given `mlt_service` **and** any transition with matching `b_track`, but these two conditions are checked across *all* transitions independently (via separate `.Any()` calls on the same collection). This means if a `mix` transition exists for a *different* track and a `qtblend` for *this* track exists, `HasTransition("mix")` can return true even though no mix exists for this track. The two `Any` predicates must be combined into a single `t => ...` predicate. ```csharp bool HasTransition(string mltService) => mainTractor.Transition.Any(t => t.Property.Any(p => p.Name == "mlt_service" && p.Text == mltService) && t.Property.Any(p => p.Name == "b_track" && p.Text == trackIndex.ToString())); ``` Actually this *is* combined per-transition correctly — disregard. On re-read the predicate is fine. 🟡 ShotcutService.cs — `duration > longestDuration` uses `playlist.Entry.Sum(e => e.Duration)`, while `SetRenderRangeToPlaylistDuration` presumably computes duration independently. If they diverge (e.g. blanks not counted in `Entry.Sum`), the render range may not match the longest cell. Worth confirming both use the same duration basis. 🟢 Home.razor — `_gridLayout` uses raw ints (1/2/4) as a magic-value selector; harmless but the value `4` implicitly means "2×2" with no guard if a future option adds `3`. `GenerateGridCompilation` correctly throws on unsupported values, so runtime is safe. 🟢 ShotcutService.cs — Filter `Id = $"gridCell{trackIndex}"` and transition ids assume `trackIndex` uniqueness across cells; since each `GenerateRandomPlaylist` returns a distinct index this holds, but there's no assertion. Minor. 4. **Verdict**: **APPROVE** The transition-matching logic is correct on re-read, and the remaining points are minor. Before merging, verify that `playlist.Entry.Sum(e => e.Duration)` and `SetRenderRangeToPlaylistDuration` compute duration the same way (including blanks), since a mismatch would clip the longest cell — but this is not blocking. --- 📊 **Tokens**: 5873 input + 929 output | 💰 **Cost**: ~$0.0526 *Generated by Claude Opus 4.8 via Gitea Actions*
merge master
All checks were successful
Claude PR Review / AI Code Review (pull_request) Successful in 20s
Build & Test / Build and Test (pull_request) Successful in 29s
d2c3600e14
cheapnud merged commit ad4e0a7721 into master 2026-08-17 10:23:26 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
cheapnud/CheapShotcutRandomizer!18
No description provided.