refactor: update media processing to use width and height instead of aspect ratio

This commit is contained in:
Maze Winther
2025-07-01 01:13:14 +02:00
parent 9b37ce6610
commit 1a01871cfc
6 changed files with 335 additions and 310 deletions

View File

@ -5,7 +5,11 @@ import {
type TimelineClip, type TimelineClip,
type TimelineTrack, type TimelineTrack,
} from "@/stores/timeline-store"; } from "@/stores/timeline-store";
import { useMediaStore, type MediaItem } from "@/stores/media-store"; import {
useMediaStore,
type MediaItem,
getMediaAspectRatio,
} from "@/stores/media-store";
import { usePlaybackStore } from "@/stores/playback-store"; import { usePlaybackStore } from "@/stores/playback-store";
import { useEditorStore } from "@/stores/editor-store"; import { useEditorStore } from "@/stores/editor-store";
import { VideoPlayer } from "@/components/ui/video-player"; import { VideoPlayer } from "@/components/ui/video-player";
@ -262,7 +266,7 @@ function PreviewToolbar({ hasAnyClips }: { hasAnyClips: boolean }) {
mediaItem && mediaItem &&
(mediaItem.type === "video" || mediaItem.type === "image") (mediaItem.type === "video" || mediaItem.type === "image")
) { ) {
return mediaItem.aspectRatio || 16 / 9; // Default to 16:9 if aspectRatio not available return getMediaAspectRatio(mediaItem);
} }
} }
} }

View File

@ -3,7 +3,7 @@ import {
getFileType, getFileType,
generateVideoThumbnail, generateVideoThumbnail,
getMediaDuration, getMediaDuration,
getImageAspectRatio, getImageDimensions,
type MediaItem, type MediaItem,
} from "@/stores/media-store"; } from "@/stores/media-store";
// import { generateThumbnail, getVideoInfo } from "./ffmpeg-utils"; // Temporarily disabled // import { generateThumbnail, getVideoInfo } from "./ffmpeg-utils"; // Temporarily disabled
@ -31,20 +31,23 @@ export async function processMediaFiles(
const url = URL.createObjectURL(file); const url = URL.createObjectURL(file);
let thumbnailUrl: string | undefined; let thumbnailUrl: string | undefined;
let duration: number | undefined; let duration: number | undefined;
let aspectRatio: number = 16 / 9; // Default fallback let width: number | undefined;
let height: number | undefined;
try { try {
if (fileType === "image") { if (fileType === "image") {
// Get image aspect ratio // Get image dimensions
aspectRatio = await getImageAspectRatio(file); const dimensions = await getImageDimensions(file);
width = dimensions.width;
height = dimensions.height;
} else if (fileType === "video") { } else if (fileType === "video") {
// Use basic thumbnail generation for now // Use basic thumbnail generation for now
const videoResult = await generateVideoThumbnail(file); const videoResult = await generateVideoThumbnail(file);
thumbnailUrl = videoResult.thumbnailUrl; thumbnailUrl = videoResult.thumbnailUrl;
aspectRatio = videoResult.aspectRatio; width = videoResult.width;
height = videoResult.height;
} else if (fileType === "audio") { } else if (fileType === "audio") {
// For audio, use a square aspect ratio // For audio, we don't set width/height (they'll be undefined)
aspectRatio = 1;
} }
// Get duration for videos and audio (if not already set by FFmpeg) // Get duration for videos and audio (if not already set by FFmpeg)
@ -59,7 +62,8 @@ export async function processMediaFiles(
url, url,
thumbnailUrl, thumbnailUrl,
duration, duration,
aspectRatio, width,
height,
}); });
// Yield back to the event loop to keep the UI responsive // Yield back to the event loop to keep the UI responsive

View File

@ -94,7 +94,8 @@ class StorageService {
type: mediaItem.type, type: mediaItem.type,
size: mediaItem.file.size, size: mediaItem.file.size,
lastModified: mediaItem.file.lastModified, lastModified: mediaItem.file.lastModified,
aspectRatio: mediaItem.aspectRatio, width: mediaItem.width,
height: mediaItem.height,
duration: mediaItem.duration, duration: mediaItem.duration,
}; };
@ -118,7 +119,8 @@ class StorageService {
type: metadata.type, type: metadata.type,
file, file,
url, url,
aspectRatio: metadata.aspectRatio, width: metadata.width,
height: metadata.height,
duration: metadata.duration, duration: metadata.duration,
// thumbnailUrl would need to be regenerated or cached separately // thumbnailUrl would need to be regenerated or cached separately
}; };

View File

@ -14,7 +14,8 @@ export interface MediaFileData {
type: "image" | "video" | "audio"; type: "image" | "video" | "audio";
size: number; size: number;
lastModified: number; lastModified: number;
aspectRatio: number; width?: number;
height?: number;
duration?: number; duration?: number;
// File will be stored separately in OPFS // File will be stored separately in OPFS
} }

View File

@ -9,7 +9,8 @@ export interface MediaItem {
url: string; // Object URL for preview url: string; // Object URL for preview
thumbnailUrl?: string; // For video thumbnails thumbnailUrl?: string; // For video thumbnails
duration?: number; // For video/audio duration duration?: number; // For video/audio duration
aspectRatio: number; // width / height width?: number; // For video/image width
height?: number; // For video/image height
} }
interface MediaStore { interface MediaStore {
@ -40,14 +41,17 @@ export const getFileType = (file: File): "image" | "video" | "audio" | null => {
return null; return null;
}; };
// Helper function to get image aspect ratio // Helper function to get image dimensions
export const getImageAspectRatio = (file: File): Promise<number> => { export const getImageDimensions = (
file: File
): Promise<{ width: number; height: number }> => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const img = new Image(); const img = new Image();
img.addEventListener("load", () => { img.addEventListener("load", () => {
const aspectRatio = img.naturalWidth / img.naturalHeight; const width = img.naturalWidth;
resolve(aspectRatio); const height = img.naturalHeight;
resolve({ width, height });
img.remove(); img.remove();
}); });
@ -60,10 +64,10 @@ export const getImageAspectRatio = (file: File): Promise<number> => {
}); });
}; };
// Helper function to generate video thumbnail and get aspect ratio // Helper function to generate video thumbnail and get dimensions
export const generateVideoThumbnail = ( export const generateVideoThumbnail = (
file: File file: File
): Promise<{ thumbnailUrl: string; aspectRatio: number }> => { ): Promise<{ thumbnailUrl: string; width: number; height: number }> => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const video = document.createElement("video"); const video = document.createElement("video");
const canvas = document.createElement("canvas"); const canvas = document.createElement("canvas");
@ -85,9 +89,10 @@ export const generateVideoThumbnail = (
video.addEventListener("seeked", () => { video.addEventListener("seeked", () => {
ctx.drawImage(video, 0, 0, canvas.width, canvas.height); ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
const thumbnailUrl = canvas.toDataURL("image/jpeg", 0.8); const thumbnailUrl = canvas.toDataURL("image/jpeg", 0.8);
const aspectRatio = video.videoWidth / video.videoHeight; const width = video.videoWidth;
const height = video.videoHeight;
resolve({ thumbnailUrl, aspectRatio }); resolve({ thumbnailUrl, width, height });
// Cleanup // Cleanup
video.remove(); video.remove();
@ -127,6 +132,14 @@ export const getMediaDuration = (file: File): Promise<number> => {
}); });
}; };
// Helper to get aspect ratio from MediaItem
export const getMediaAspectRatio = (item: MediaItem): number => {
if (item.width && item.height) {
return item.width / item.height;
}
return 16 / 9; // Default aspect ratio
};
export const useMediaStore = create<MediaStore>((set, get) => ({ export const useMediaStore = create<MediaStore>((set, get) => ({
mediaItems: [], mediaItems: [],
isLoading: false, isLoading: false,

View File

@ -1,8 +1,7 @@
import { create } from "zustand"; import { create } from "zustand";
import type { TrackType } from "@/types/timeline"; import type { TrackType } from "@/types/timeline";
import { useEditorStore } from "./editor-store"; import { useEditorStore } from "./editor-store";
import { useMediaStore } from "./media-store"; import { useMediaStore, getMediaAspectRatio } from "./media-store";
import { toast } from "sonner";
// Helper function to manage clip naming with suffixes // Helper function to manage clip naming with suffixes
const getClipNameWithSuffix = ( const getClipNameWithSuffix = (
@ -232,7 +231,9 @@ export const useTimelineStore = create<TimelineStore>((set, get) => ({
(mediaItem.type === "image" || mediaItem.type === "video") (mediaItem.type === "image" || mediaItem.type === "video")
) { ) {
const editorStore = useEditorStore.getState(); const editorStore = useEditorStore.getState();
editorStore.setCanvasSizeFromAspectRatio(mediaItem.aspectRatio); editorStore.setCanvasSizeFromAspectRatio(
getMediaAspectRatio(mediaItem)
);
} }
} }