diff options
| author | Maze Winther <mazewinther@gmail.com> | 2026-03-31 15:53:23 +0200 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2026-03-31 15:53:23 +0200 |
| commit | d23be079cce6108482fc3e85401cccf949087174 (patch) | |
| tree | 7d5acd324f9274b3b823bc67154349b8693e3412 /apps/web/src/lib/timeline/placement/main-track.ts | |
| parent | dcf54c1250431f5a4a2848f7c07781ef5eebacaf (diff) | |
| parent | 70a6d58b8432e455ab1443d45bc23dd70b363bea (diff) | |
| download | opencut-d23be079cce6108482fc3e85401cccf949087174.tar.gz | |
Merge branch 'feature/track-placement'
Diffstat (limited to 'apps/web/src/lib/timeline/placement/main-track.ts')
| -rw-r--r-- | apps/web/src/lib/timeline/placement/main-track.ts | 96 |
1 files changed, 96 insertions, 0 deletions
diff --git a/apps/web/src/lib/timeline/placement/main-track.ts b/apps/web/src/lib/timeline/placement/main-track.ts new file mode 100644 index 00000000..1783352c --- /dev/null +++ b/apps/web/src/lib/timeline/placement/main-track.ts @@ -0,0 +1,96 @@ +import type { TimelineElement, TimelineTrack, VideoTrack } from "@/lib/timeline"; +import { generateUUID } from "@/utils/id"; + +const MAIN_TRACK_NAME = "Main Track"; + +export function isMainTrack(track: TimelineTrack): track is VideoTrack { + return track.type === "video" && track.isMain === true; +} + +export function getMainTrack({ + tracks, +}: { + tracks: TimelineTrack[]; +}): VideoTrack | null { + return tracks.find((track) => isMainTrack(track)) ?? null; +} + +export function ensureMainTrack({ + tracks, +}: { + tracks: TimelineTrack[]; +}): TimelineTrack[] { + if (tracks.some((track) => isMainTrack(track))) { + return tracks; + } + + return [ + { + id: generateUUID(), + name: MAIN_TRACK_NAME, + type: "video", + elements: [], + muted: false, + isMain: true, + hidden: false, + }, + ...tracks, + ]; +} + +export function getEarliestMainTrackElement({ + tracks, + excludeElementId, +}: { + tracks: TimelineTrack[]; + excludeElementId?: string; +}): TimelineElement | null { + const mainTrack = getMainTrack({ tracks }); + if (!mainTrack) { + return null; + } + + const elements = mainTrack.elements.filter((element) => { + return !excludeElementId || element.id !== excludeElementId; + }); + if (elements.length === 0) { + return null; + } + + return elements.reduce((earliestElement, element) => { + return element.startTime < earliestElement.startTime + ? element + : earliestElement; + }); +} + +export function enforceMainTrackStart({ + tracks, + targetTrackId, + requestedStartTime, + excludeElementId, +}: { + tracks: TimelineTrack[]; + targetTrackId: string; + requestedStartTime: number; + excludeElementId?: string; +}): number { + const mainTrack = getMainTrack({ tracks }); + if (!mainTrack || mainTrack.id !== targetTrackId) { + return requestedStartTime; + } + + const earliestElement = getEarliestMainTrackElement({ + tracks, + excludeElementId, + }); + if (!earliestElement) { + return 0; + } + + if (requestedStartTime <= earliestElement.startTime) { + return 0; + } + + return requestedStartTime; +} |
