mirror of
https://github.com/OpenCut-app/OpenCut.git
synced 2026-07-13 21:52:53 +02:00
lots of stuff
This commit is contained in:
@@ -1,227 +1,227 @@
|
||||
import { DEFAULT_TEXT_ELEMENT } from "@/constants/text-constants";
|
||||
import { TIMELINE_CONSTANTS } from "@/constants/timeline-constants";
|
||||
import {
|
||||
CreateTimelineElement,
|
||||
CreateStickerElement,
|
||||
CreateUploadAudioElement,
|
||||
CreateLibraryAudioElement,
|
||||
TextElement,
|
||||
TimelineElement,
|
||||
AudioElement,
|
||||
VideoElement,
|
||||
ImageElement,
|
||||
StickerElement,
|
||||
UploadAudioElement,
|
||||
import type {
|
||||
CreateTimelineElement,
|
||||
CreateStickerElement,
|
||||
CreateUploadAudioElement,
|
||||
CreateLibraryAudioElement,
|
||||
TextElement,
|
||||
TimelineElement,
|
||||
AudioElement,
|
||||
VideoElement,
|
||||
ImageElement,
|
||||
StickerElement,
|
||||
UploadAudioElement,
|
||||
} from "@/types/timeline";
|
||||
|
||||
export function canElementHaveAudio(
|
||||
element: TimelineElement,
|
||||
element: TimelineElement,
|
||||
): element is AudioElement | VideoElement {
|
||||
return element.type === "audio" || element.type === "video";
|
||||
return element.type === "audio" || element.type === "video";
|
||||
}
|
||||
|
||||
export function canElementBeHidden(
|
||||
element: TimelineElement,
|
||||
element: TimelineElement,
|
||||
): element is VideoElement | ImageElement | TextElement | StickerElement {
|
||||
return element.type !== "audio";
|
||||
return element.type !== "audio";
|
||||
}
|
||||
|
||||
export function hasMediaId(
|
||||
element: TimelineElement,
|
||||
element: TimelineElement,
|
||||
): element is UploadAudioElement | VideoElement | ImageElement {
|
||||
return "mediaId" in element;
|
||||
return "mediaId" in element;
|
||||
}
|
||||
|
||||
export function requiresMediaId({
|
||||
element,
|
||||
element,
|
||||
}: {
|
||||
element: CreateTimelineElement;
|
||||
element: CreateTimelineElement;
|
||||
}): boolean {
|
||||
return (
|
||||
element.type === "video" ||
|
||||
element.type === "image" ||
|
||||
(element.type === "audio" && element.sourceType === "upload")
|
||||
);
|
||||
return (
|
||||
element.type === "video" ||
|
||||
element.type === "image" ||
|
||||
(element.type === "audio" && element.sourceType === "upload")
|
||||
);
|
||||
}
|
||||
|
||||
export function checkElementOverlaps({
|
||||
elements,
|
||||
elements,
|
||||
}: {
|
||||
elements: TimelineElement[];
|
||||
elements: TimelineElement[];
|
||||
}): boolean {
|
||||
const sortedElements = [...elements].sort(
|
||||
(a, b) => a.startTime - b.startTime,
|
||||
);
|
||||
const sortedElements = [...elements].sort(
|
||||
(a, b) => a.startTime - b.startTime,
|
||||
);
|
||||
|
||||
for (let i = 0; i < sortedElements.length - 1; i++) {
|
||||
const current = sortedElements[i];
|
||||
const next = sortedElements[i + 1];
|
||||
for (let i = 0; i < sortedElements.length - 1; i++) {
|
||||
const current = sortedElements[i];
|
||||
const next = sortedElements[i + 1];
|
||||
|
||||
const currentEnd = current.startTime + current.duration;
|
||||
const currentEnd = current.startTime + current.duration;
|
||||
|
||||
if (currentEnd > next.startTime) return true;
|
||||
}
|
||||
if (currentEnd > next.startTime) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
export function resolveElementOverlaps({
|
||||
elements,
|
||||
elements,
|
||||
}: {
|
||||
elements: TimelineElement[];
|
||||
elements: TimelineElement[];
|
||||
}): TimelineElement[] {
|
||||
const sortedElements = [...elements].sort(
|
||||
(a, b) => a.startTime - b.startTime,
|
||||
);
|
||||
const resolvedElements: TimelineElement[] = [];
|
||||
const sortedElements = [...elements].sort(
|
||||
(a, b) => a.startTime - b.startTime,
|
||||
);
|
||||
const resolvedElements: TimelineElement[] = [];
|
||||
|
||||
for (let i = 0; i < sortedElements.length; i++) {
|
||||
const current = { ...sortedElements[i] };
|
||||
for (let i = 0; i < sortedElements.length; i++) {
|
||||
const current = { ...sortedElements[i] };
|
||||
|
||||
if (resolvedElements.length > 0) {
|
||||
const previous = resolvedElements[resolvedElements.length - 1];
|
||||
const previousEnd = previous.startTime + previous.duration;
|
||||
if (resolvedElements.length > 0) {
|
||||
const previous = resolvedElements[resolvedElements.length - 1];
|
||||
const previousEnd = previous.startTime + previous.duration;
|
||||
|
||||
if (current.startTime < previousEnd) {
|
||||
current.startTime = previousEnd;
|
||||
}
|
||||
}
|
||||
if (current.startTime < previousEnd) {
|
||||
current.startTime = previousEnd;
|
||||
}
|
||||
}
|
||||
|
||||
resolvedElements.push(current);
|
||||
}
|
||||
resolvedElements.push(current);
|
||||
}
|
||||
|
||||
return resolvedElements;
|
||||
return resolvedElements;
|
||||
}
|
||||
|
||||
export function wouldElementOverlap({
|
||||
elements,
|
||||
startTime,
|
||||
endTime,
|
||||
excludeElementId,
|
||||
elements,
|
||||
startTime,
|
||||
endTime,
|
||||
excludeElementId,
|
||||
}: {
|
||||
elements: TimelineElement[];
|
||||
startTime: number;
|
||||
endTime: number;
|
||||
excludeElementId?: string;
|
||||
elements: TimelineElement[];
|
||||
startTime: number;
|
||||
endTime: number;
|
||||
excludeElementId?: string;
|
||||
}): boolean {
|
||||
return elements.some((el) => {
|
||||
if (excludeElementId && el.id === excludeElementId) return false;
|
||||
const elEnd = el.startTime + el.duration;
|
||||
return startTime < elEnd && endTime > el.startTime;
|
||||
});
|
||||
return elements.some((el) => {
|
||||
if (excludeElementId && el.id === excludeElementId) return false;
|
||||
const elEnd = el.startTime + el.duration;
|
||||
return startTime < elEnd && endTime > el.startTime;
|
||||
});
|
||||
}
|
||||
|
||||
export function buildTextElement({
|
||||
raw,
|
||||
startTime,
|
||||
raw,
|
||||
startTime,
|
||||
}: {
|
||||
raw: Partial<Omit<TextElement, "type" | "id">>;
|
||||
startTime: number;
|
||||
raw: Partial<Omit<TextElement, "type" | "id">>;
|
||||
startTime: number;
|
||||
}): CreateTimelineElement {
|
||||
const t = raw as Partial<TextElement>;
|
||||
const t = raw as Partial<TextElement>;
|
||||
|
||||
return {
|
||||
type: "text",
|
||||
name: t.name ?? DEFAULT_TEXT_ELEMENT.name,
|
||||
content: t.content ?? DEFAULT_TEXT_ELEMENT.content,
|
||||
duration: t.duration ?? TIMELINE_CONSTANTS.DEFAULT_ELEMENT_DURATION,
|
||||
startTime,
|
||||
trimStart: 0,
|
||||
trimEnd: 0,
|
||||
fontSize:
|
||||
typeof t.fontSize === "number"
|
||||
? t.fontSize
|
||||
: DEFAULT_TEXT_ELEMENT.fontSize,
|
||||
fontFamily: t.fontFamily ?? DEFAULT_TEXT_ELEMENT.fontFamily,
|
||||
color: t.color ?? DEFAULT_TEXT_ELEMENT.color,
|
||||
backgroundColor: t.backgroundColor ?? DEFAULT_TEXT_ELEMENT.backgroundColor,
|
||||
textAlign: t.textAlign ?? DEFAULT_TEXT_ELEMENT.textAlign,
|
||||
fontWeight: t.fontWeight ?? DEFAULT_TEXT_ELEMENT.fontWeight,
|
||||
fontStyle: t.fontStyle ?? DEFAULT_TEXT_ELEMENT.fontStyle,
|
||||
textDecoration: t.textDecoration ?? DEFAULT_TEXT_ELEMENT.textDecoration,
|
||||
transform: t.transform ?? DEFAULT_TEXT_ELEMENT.transform,
|
||||
opacity: t.opacity ?? DEFAULT_TEXT_ELEMENT.opacity,
|
||||
};
|
||||
return {
|
||||
type: "text",
|
||||
name: t.name ?? DEFAULT_TEXT_ELEMENT.name,
|
||||
content: t.content ?? DEFAULT_TEXT_ELEMENT.content,
|
||||
duration: t.duration ?? TIMELINE_CONSTANTS.DEFAULT_ELEMENT_DURATION,
|
||||
startTime,
|
||||
trimStart: 0,
|
||||
trimEnd: 0,
|
||||
fontSize:
|
||||
typeof t.fontSize === "number"
|
||||
? t.fontSize
|
||||
: DEFAULT_TEXT_ELEMENT.fontSize,
|
||||
fontFamily: t.fontFamily ?? DEFAULT_TEXT_ELEMENT.fontFamily,
|
||||
color: t.color ?? DEFAULT_TEXT_ELEMENT.color,
|
||||
backgroundColor: t.backgroundColor ?? DEFAULT_TEXT_ELEMENT.backgroundColor,
|
||||
textAlign: t.textAlign ?? DEFAULT_TEXT_ELEMENT.textAlign,
|
||||
fontWeight: t.fontWeight ?? DEFAULT_TEXT_ELEMENT.fontWeight,
|
||||
fontStyle: t.fontStyle ?? DEFAULT_TEXT_ELEMENT.fontStyle,
|
||||
textDecoration: t.textDecoration ?? DEFAULT_TEXT_ELEMENT.textDecoration,
|
||||
transform: t.transform ?? DEFAULT_TEXT_ELEMENT.transform,
|
||||
opacity: t.opacity ?? DEFAULT_TEXT_ELEMENT.opacity,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildStickerElement({
|
||||
iconName,
|
||||
startTime,
|
||||
iconName,
|
||||
startTime,
|
||||
}: {
|
||||
iconName: string;
|
||||
startTime: number;
|
||||
iconName: string;
|
||||
startTime: number;
|
||||
}): CreateStickerElement {
|
||||
return {
|
||||
type: "sticker",
|
||||
name: iconName.split(":")[1] || iconName,
|
||||
iconName,
|
||||
duration: TIMELINE_CONSTANTS.DEFAULT_ELEMENT_DURATION,
|
||||
startTime,
|
||||
trimStart: 0,
|
||||
trimEnd: 0,
|
||||
transform: { scale: 1, position: { x: 0, y: 0 }, rotate: 0 },
|
||||
opacity: 1,
|
||||
};
|
||||
return {
|
||||
type: "sticker",
|
||||
name: iconName.split(":")[1] || iconName,
|
||||
iconName,
|
||||
duration: TIMELINE_CONSTANTS.DEFAULT_ELEMENT_DURATION,
|
||||
startTime,
|
||||
trimStart: 0,
|
||||
trimEnd: 0,
|
||||
transform: { scale: 1, position: { x: 0, y: 0 }, rotate: 0 },
|
||||
opacity: 1,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildUploadAudioElement({
|
||||
mediaId,
|
||||
name,
|
||||
duration,
|
||||
startTime,
|
||||
buffer,
|
||||
mediaId,
|
||||
name,
|
||||
duration,
|
||||
startTime,
|
||||
buffer,
|
||||
}: {
|
||||
mediaId: string;
|
||||
name: string;
|
||||
duration: number;
|
||||
startTime: number;
|
||||
buffer?: AudioBuffer;
|
||||
mediaId: string;
|
||||
name: string;
|
||||
duration: number;
|
||||
startTime: number;
|
||||
buffer?: AudioBuffer;
|
||||
}): CreateUploadAudioElement {
|
||||
const element: CreateUploadAudioElement = {
|
||||
type: "audio",
|
||||
sourceType: "upload",
|
||||
mediaId,
|
||||
name,
|
||||
duration,
|
||||
startTime,
|
||||
trimStart: 0,
|
||||
trimEnd: 0,
|
||||
volume: 1,
|
||||
muted: false,
|
||||
};
|
||||
if (buffer) {
|
||||
element.buffer = buffer;
|
||||
}
|
||||
return element;
|
||||
const element: CreateUploadAudioElement = {
|
||||
type: "audio",
|
||||
sourceType: "upload",
|
||||
mediaId,
|
||||
name,
|
||||
duration,
|
||||
startTime,
|
||||
trimStart: 0,
|
||||
trimEnd: 0,
|
||||
volume: 1,
|
||||
muted: false,
|
||||
};
|
||||
if (buffer) {
|
||||
element.buffer = buffer;
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
export function buildLibraryAudioElement({
|
||||
sourceUrl,
|
||||
name,
|
||||
duration,
|
||||
startTime,
|
||||
buffer,
|
||||
sourceUrl,
|
||||
name,
|
||||
duration,
|
||||
startTime,
|
||||
buffer,
|
||||
}: {
|
||||
sourceUrl: string;
|
||||
name: string;
|
||||
duration: number;
|
||||
startTime: number;
|
||||
buffer?: AudioBuffer;
|
||||
sourceUrl: string;
|
||||
name: string;
|
||||
duration: number;
|
||||
startTime: number;
|
||||
buffer?: AudioBuffer;
|
||||
}): CreateLibraryAudioElement {
|
||||
const element: CreateLibraryAudioElement = {
|
||||
type: "audio",
|
||||
sourceType: "library",
|
||||
sourceUrl,
|
||||
name,
|
||||
duration,
|
||||
startTime,
|
||||
trimStart: 0,
|
||||
trimEnd: 0,
|
||||
volume: 1,
|
||||
muted: false,
|
||||
};
|
||||
if (buffer) {
|
||||
element.buffer = buffer;
|
||||
}
|
||||
return element;
|
||||
const element: CreateLibraryAudioElement = {
|
||||
type: "audio",
|
||||
sourceType: "library",
|
||||
sourceUrl,
|
||||
name,
|
||||
duration,
|
||||
startTime,
|
||||
trimStart: 0,
|
||||
trimEnd: 0,
|
||||
volume: 1,
|
||||
muted: false,
|
||||
};
|
||||
if (buffer) {
|
||||
element.buffer = buffer;
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user