diff options
| author | Maze Winther <mazewinther@gmail.com> | 2026-03-27 00:55:15 +0100 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2026-03-29 15:56:48 +0200 |
| commit | 4d127a038bf768eded68aae3cfa5c1b12241bdac (patch) | |
| tree | 16bbb801caf6770546cff4fdb37b6408ec9ead98 /apps/web/src/lib/project | |
| parent | 82817cb50cb0487350573d6129130aec705f924b (diff) | |
| download | opencut-4d127a038bf768eded68aae3cfa5c1b12241bdac.tar.gz | |
chore: normalize line endings
Diffstat (limited to 'apps/web/src/lib/project')
| -rw-r--r-- | apps/web/src/lib/project/__tests__/fps.test.ts | 65 | ||||
| -rw-r--r-- | apps/web/src/lib/project/fps.ts | 39 | ||||
| -rw-r--r-- | apps/web/src/lib/project/types.ts | 106 |
3 files changed, 157 insertions, 53 deletions
diff --git a/apps/web/src/lib/project/__tests__/fps.test.ts b/apps/web/src/lib/project/__tests__/fps.test.ts new file mode 100644 index 00000000..b30408ef --- /dev/null +++ b/apps/web/src/lib/project/__tests__/fps.test.ts @@ -0,0 +1,65 @@ +import { describe, expect, test } from "bun:test";
+import {
+ getHighestImportedVideoFps,
+ getRaisedProjectFpsForImportedMedia,
+} from "@/lib/project/fps";
+
+describe("getHighestImportedVideoFps", () => {
+ test("returns the highest valid video fps", () => {
+ expect(
+ getHighestImportedVideoFps({
+ mediaAssets: [
+ { type: "audio" },
+ { type: "video", fps: 30 },
+ { type: "image", fps: 120 },
+ { type: "video", fps: 60 },
+ ],
+ }),
+ ).toBe(60);
+ });
+
+ test("ignores missing and invalid fps values", () => {
+ expect(
+ getHighestImportedVideoFps({
+ mediaAssets: [
+ { type: "video" },
+ { type: "video", fps: 0 },
+ { type: "video", fps: -10 },
+ { type: "audio", fps: 120 },
+ ],
+ }),
+ ).toBeNull();
+ });
+});
+
+describe("getRaisedProjectFpsForImportedMedia", () => {
+ test("raises the project fps to match a higher-fps import", () => {
+ expect(
+ getRaisedProjectFpsForImportedMedia({
+ currentFps: 30,
+ importedAssets: [{ type: "video", fps: 60 }],
+ }),
+ ).toBe(60);
+ });
+
+ test("does not lower the project fps for lower-fps imports", () => {
+ expect(
+ getRaisedProjectFpsForImportedMedia({
+ currentFps: 60,
+ importedAssets: [{ type: "video", fps: 10 }],
+ }),
+ ).toBeNull();
+ });
+
+ test("ignores non-video imports", () => {
+ expect(
+ getRaisedProjectFpsForImportedMedia({
+ currentFps: 30,
+ importedAssets: [
+ { type: "image", fps: 60 },
+ { type: "audio", fps: 120 },
+ ],
+ }),
+ ).toBeNull();
+ });
+});
diff --git a/apps/web/src/lib/project/fps.ts b/apps/web/src/lib/project/fps.ts new file mode 100644 index 00000000..f031d348 --- /dev/null +++ b/apps/web/src/lib/project/fps.ts @@ -0,0 +1,39 @@ +import type { MediaAsset } from "@/lib/media/types";
+
+type MediaAssetFpsInput = Pick<MediaAsset, "type" | "fps">;
+
+export function getHighestImportedVideoFps({
+ mediaAssets,
+}: {
+ mediaAssets: MediaAssetFpsInput[];
+}): number | null {
+ let highestFps: number | null = null;
+
+ for (const asset of mediaAssets) {
+ const fps = asset.fps ?? Number.NaN;
+ if (asset.type !== "video") continue;
+ if (!Number.isFinite(fps) || fps <= 0) continue;
+
+ highestFps = highestFps === null ? fps : Math.max(highestFps, fps);
+ }
+
+ return highestFps;
+}
+
+export function getRaisedProjectFpsForImportedMedia({
+ currentFps,
+ importedAssets,
+}: {
+ currentFps: number;
+ importedAssets: MediaAssetFpsInput[];
+}): number | null {
+ const highestImportedVideoFps = getHighestImportedVideoFps({
+ mediaAssets: importedAssets,
+ });
+
+ if (highestImportedVideoFps === null || highestImportedVideoFps <= currentFps) {
+ return null;
+ }
+
+ return highestImportedVideoFps;
+}
diff --git a/apps/web/src/lib/project/types.ts b/apps/web/src/lib/project/types.ts index 734c3835..8f45db9e 100644 --- a/apps/web/src/lib/project/types.ts +++ b/apps/web/src/lib/project/types.ts @@ -1,53 +1,53 @@ -import type { TScene } from "@/lib/timeline/types"; - -export type TBackground = - | { - type: "color"; - color: string; - } - | { - type: "blur"; - blurIntensity: number; - }; - -export interface TCanvasSize { - width: number; - height: number; -} - -export interface TProjectMetadata { - id: string; - name: string; - thumbnail?: string; - duration: number; - createdAt: Date; - updatedAt: Date; -} - -export interface TProjectSettings { - fps: number; - canvasSize: TCanvasSize; - canvasSizeMode?: "preset" | "custom"; - lastCustomCanvasSize?: TCanvasSize | null; - originalCanvasSize?: TCanvasSize | null; - background: TBackground; -} - -export interface TTimelineViewState { - zoomLevel: number; - scrollLeft: number; - playheadTime: number; -} - -export interface TProject { - metadata: TProjectMetadata; - scenes: TScene[]; - currentSceneId: string; - settings: TProjectSettings; - version: number; - timelineViewState?: TTimelineViewState; -} - -export type TProjectSortKey = "createdAt" | "updatedAt" | "name" | "duration"; -export type TSortOrder = "asc" | "desc"; -export type TProjectSortOption = `${TProjectSortKey}-${TSortOrder}`; +import type { TScene } from "@/lib/timeline/types";
+
+export type TBackground =
+ | {
+ type: "color";
+ color: string;
+ }
+ | {
+ type: "blur";
+ blurIntensity: number;
+ };
+
+export interface TCanvasSize {
+ width: number;
+ height: number;
+}
+
+export interface TProjectMetadata {
+ id: string;
+ name: string;
+ thumbnail?: string;
+ duration: number;
+ createdAt: Date;
+ updatedAt: Date;
+}
+
+export interface TProjectSettings {
+ fps: number;
+ canvasSize: TCanvasSize;
+ canvasSizeMode?: "preset" | "custom";
+ lastCustomCanvasSize?: TCanvasSize | null;
+ originalCanvasSize?: TCanvasSize | null;
+ background: TBackground;
+}
+
+export interface TTimelineViewState {
+ zoomLevel: number;
+ scrollLeft: number;
+ playheadTime: number;
+}
+
+export interface TProject {
+ metadata: TProjectMetadata;
+ scenes: TScene[];
+ currentSceneId: string;
+ settings: TProjectSettings;
+ version: number;
+ timelineViewState?: TTimelineViewState;
+}
+
+export type TProjectSortKey = "createdAt" | "updatedAt" | "name" | "duration";
+export type TSortOrder = "asc" | "desc";
+export type TProjectSortOption = `${TProjectSortKey}-${TSortOrder}`;
|
