refactor: update media processing to use width and height instead of aspect ratio
This commit is contained in:
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,81 +1,85 @@
|
|||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import {
|
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
|
||||||
|
|
||||||
export interface ProcessedMediaItem extends Omit<MediaItem, "id"> {}
|
export interface ProcessedMediaItem extends Omit<MediaItem, "id"> {}
|
||||||
|
|
||||||
export async function processMediaFiles(
|
export async function processMediaFiles(
|
||||||
files: FileList | File[],
|
files: FileList | File[],
|
||||||
onProgress?: (progress: number) => void
|
onProgress?: (progress: number) => void
|
||||||
): Promise<ProcessedMediaItem[]> {
|
): Promise<ProcessedMediaItem[]> {
|
||||||
const fileArray = Array.from(files);
|
const fileArray = Array.from(files);
|
||||||
const processedItems: ProcessedMediaItem[] = [];
|
const processedItems: ProcessedMediaItem[] = [];
|
||||||
|
|
||||||
const total = fileArray.length;
|
const total = fileArray.length;
|
||||||
let completed = 0;
|
let completed = 0;
|
||||||
|
|
||||||
for (const file of fileArray) {
|
for (const file of fileArray) {
|
||||||
const fileType = getFileType(file);
|
const fileType = getFileType(file);
|
||||||
|
|
||||||
if (!fileType) {
|
if (!fileType) {
|
||||||
toast.error(`Unsupported file type: ${file.name}`);
|
toast.error(`Unsupported file type: ${file.name}`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
|
||||||
if (fileType === "image") {
|
try {
|
||||||
// Get image aspect ratio
|
if (fileType === "image") {
|
||||||
aspectRatio = await getImageAspectRatio(file);
|
// Get image dimensions
|
||||||
} else if (fileType === "video") {
|
const dimensions = await getImageDimensions(file);
|
||||||
// Use basic thumbnail generation for now
|
width = dimensions.width;
|
||||||
const videoResult = await generateVideoThumbnail(file);
|
height = dimensions.height;
|
||||||
thumbnailUrl = videoResult.thumbnailUrl;
|
} else if (fileType === "video") {
|
||||||
aspectRatio = videoResult.aspectRatio;
|
// Use basic thumbnail generation for now
|
||||||
} else if (fileType === "audio") {
|
const videoResult = await generateVideoThumbnail(file);
|
||||||
// For audio, use a square aspect ratio
|
thumbnailUrl = videoResult.thumbnailUrl;
|
||||||
aspectRatio = 1;
|
width = videoResult.width;
|
||||||
}
|
height = videoResult.height;
|
||||||
|
} else if (fileType === "audio") {
|
||||||
// Get duration for videos and audio (if not already set by FFmpeg)
|
// For audio, we don't set width/height (they'll be undefined)
|
||||||
if ((fileType === "video" || fileType === "audio") && !duration) {
|
}
|
||||||
duration = await getMediaDuration(file);
|
|
||||||
}
|
// Get duration for videos and audio (if not already set by FFmpeg)
|
||||||
|
if ((fileType === "video" || fileType === "audio") && !duration) {
|
||||||
processedItems.push({
|
duration = await getMediaDuration(file);
|
||||||
name: file.name,
|
}
|
||||||
type: fileType,
|
|
||||||
file,
|
processedItems.push({
|
||||||
url,
|
name: file.name,
|
||||||
thumbnailUrl,
|
type: fileType,
|
||||||
duration,
|
file,
|
||||||
aspectRatio,
|
url,
|
||||||
});
|
thumbnailUrl,
|
||||||
|
duration,
|
||||||
// Yield back to the event loop to keep the UI responsive
|
width,
|
||||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
height,
|
||||||
|
});
|
||||||
completed += 1;
|
|
||||||
if (onProgress) {
|
// Yield back to the event loop to keep the UI responsive
|
||||||
const percent = Math.round((completed / total) * 100);
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||||
onProgress(percent);
|
|
||||||
}
|
completed += 1;
|
||||||
} catch (error) {
|
if (onProgress) {
|
||||||
console.error("Error processing file:", file.name, error);
|
const percent = Math.round((completed / total) * 100);
|
||||||
toast.error(`Failed to process ${file.name}`);
|
onProgress(percent);
|
||||||
URL.revokeObjectURL(url); // Clean up on error
|
}
|
||||||
}
|
} catch (error) {
|
||||||
}
|
console.error("Error processing file:", file.name, error);
|
||||||
|
toast.error(`Failed to process ${file.name}`);
|
||||||
return processedItems;
|
URL.revokeObjectURL(url); // Clean up on error
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return processedItems;
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
};
|
};
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
@ -38,4 +39,4 @@ declare global {
|
|||||||
values(): AsyncIterableIterator<FileSystemHandle>;
|
values(): AsyncIterableIterator<FileSystemHandle>;
|
||||||
entries(): AsyncIterableIterator<[string, FileSystemHandle]>;
|
entries(): AsyncIterableIterator<[string, FileSystemHandle]>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,220 +1,233 @@
|
|||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { storageService } from "@/lib/storage/storage-service";
|
import { storageService } from "@/lib/storage/storage-service";
|
||||||
|
|
||||||
export interface MediaItem {
|
export interface MediaItem {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
type: "image" | "video" | "audio";
|
type: "image" | "video" | "audio";
|
||||||
file: File;
|
file: File;
|
||||||
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 {
|
|
||||||
mediaItems: MediaItem[];
|
interface MediaStore {
|
||||||
isLoading: boolean;
|
mediaItems: MediaItem[];
|
||||||
|
isLoading: boolean;
|
||||||
// Actions
|
|
||||||
addMediaItem: (item: Omit<MediaItem, "id">) => Promise<void>;
|
// Actions
|
||||||
removeMediaItem: (id: string) => Promise<void>;
|
addMediaItem: (item: Omit<MediaItem, "id">) => Promise<void>;
|
||||||
loadAllMedia: () => Promise<void>;
|
removeMediaItem: (id: string) => Promise<void>;
|
||||||
clearAllMedia: () => Promise<void>;
|
loadAllMedia: () => Promise<void>;
|
||||||
}
|
clearAllMedia: () => Promise<void>;
|
||||||
|
}
|
||||||
// Helper function to determine file type
|
|
||||||
export const getFileType = (file: File): "image" | "video" | "audio" | null => {
|
// Helper function to determine file type
|
||||||
const { type } = file;
|
export const getFileType = (file: File): "image" | "video" | "audio" | null => {
|
||||||
|
const { type } = file;
|
||||||
if (type.startsWith("image/")) {
|
|
||||||
return "image";
|
if (type.startsWith("image/")) {
|
||||||
}
|
return "image";
|
||||||
if (type.startsWith("video/")) {
|
}
|
||||||
return "video";
|
if (type.startsWith("video/")) {
|
||||||
}
|
return "video";
|
||||||
if (type.startsWith("audio/")) {
|
}
|
||||||
return "audio";
|
if (type.startsWith("audio/")) {
|
||||||
}
|
return "audio";
|
||||||
|
}
|
||||||
return null;
|
|
||||||
};
|
return null;
|
||||||
|
};
|
||||||
// Helper function to get image aspect ratio
|
|
||||||
export const getImageAspectRatio = (file: File): Promise<number> => {
|
// Helper function to get image dimensions
|
||||||
return new Promise((resolve, reject) => {
|
export const getImageDimensions = (
|
||||||
const img = new Image();
|
file: File
|
||||||
|
): Promise<{ width: number; height: number }> => {
|
||||||
img.addEventListener("load", () => {
|
return new Promise((resolve, reject) => {
|
||||||
const aspectRatio = img.naturalWidth / img.naturalHeight;
|
const img = new Image();
|
||||||
resolve(aspectRatio);
|
|
||||||
img.remove();
|
img.addEventListener("load", () => {
|
||||||
});
|
const width = img.naturalWidth;
|
||||||
|
const height = img.naturalHeight;
|
||||||
img.addEventListener("error", () => {
|
resolve({ width, height });
|
||||||
reject(new Error("Could not load image"));
|
img.remove();
|
||||||
img.remove();
|
});
|
||||||
});
|
|
||||||
|
img.addEventListener("error", () => {
|
||||||
img.src = URL.createObjectURL(file);
|
reject(new Error("Could not load image"));
|
||||||
});
|
img.remove();
|
||||||
};
|
});
|
||||||
|
|
||||||
// Helper function to generate video thumbnail and get aspect ratio
|
img.src = URL.createObjectURL(file);
|
||||||
export const generateVideoThumbnail = (
|
});
|
||||||
file: File
|
};
|
||||||
): Promise<{ thumbnailUrl: string; aspectRatio: number }> => {
|
|
||||||
return new Promise((resolve, reject) => {
|
// Helper function to generate video thumbnail and get dimensions
|
||||||
const video = document.createElement("video");
|
export const generateVideoThumbnail = (
|
||||||
const canvas = document.createElement("canvas");
|
file: File
|
||||||
const ctx = canvas.getContext("2d");
|
): Promise<{ thumbnailUrl: string; width: number; height: number }> => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
if (!ctx) {
|
const video = document.createElement("video");
|
||||||
reject(new Error("Could not get canvas context"));
|
const canvas = document.createElement("canvas");
|
||||||
return;
|
const ctx = canvas.getContext("2d");
|
||||||
}
|
|
||||||
|
if (!ctx) {
|
||||||
video.addEventListener("loadedmetadata", () => {
|
reject(new Error("Could not get canvas context"));
|
||||||
canvas.width = video.videoWidth;
|
return;
|
||||||
canvas.height = video.videoHeight;
|
}
|
||||||
|
|
||||||
// Seek to 1 second or 10% of duration, whichever is smaller
|
video.addEventListener("loadedmetadata", () => {
|
||||||
video.currentTime = Math.min(1, video.duration * 0.1);
|
canvas.width = video.videoWidth;
|
||||||
});
|
canvas.height = video.videoHeight;
|
||||||
|
|
||||||
video.addEventListener("seeked", () => {
|
// Seek to 1 second or 10% of duration, whichever is smaller
|
||||||
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
video.currentTime = Math.min(1, video.duration * 0.1);
|
||||||
const thumbnailUrl = canvas.toDataURL("image/jpeg", 0.8);
|
});
|
||||||
const aspectRatio = video.videoWidth / video.videoHeight;
|
|
||||||
|
video.addEventListener("seeked", () => {
|
||||||
resolve({ thumbnailUrl, aspectRatio });
|
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
||||||
|
const thumbnailUrl = canvas.toDataURL("image/jpeg", 0.8);
|
||||||
// Cleanup
|
const width = video.videoWidth;
|
||||||
video.remove();
|
const height = video.videoHeight;
|
||||||
canvas.remove();
|
|
||||||
});
|
resolve({ thumbnailUrl, width, height });
|
||||||
|
|
||||||
video.addEventListener("error", () => {
|
// Cleanup
|
||||||
reject(new Error("Could not load video"));
|
video.remove();
|
||||||
video.remove();
|
canvas.remove();
|
||||||
canvas.remove();
|
});
|
||||||
});
|
|
||||||
|
video.addEventListener("error", () => {
|
||||||
video.src = URL.createObjectURL(file);
|
reject(new Error("Could not load video"));
|
||||||
video.load();
|
video.remove();
|
||||||
});
|
canvas.remove();
|
||||||
};
|
});
|
||||||
|
|
||||||
// Helper function to get media duration
|
video.src = URL.createObjectURL(file);
|
||||||
export const getMediaDuration = (file: File): Promise<number> => {
|
video.load();
|
||||||
return new Promise((resolve, reject) => {
|
});
|
||||||
const element = document.createElement(
|
};
|
||||||
file.type.startsWith("video/") ? "video" : "audio"
|
|
||||||
) as HTMLVideoElement | HTMLAudioElement;
|
// Helper function to get media duration
|
||||||
|
export const getMediaDuration = (file: File): Promise<number> => {
|
||||||
element.addEventListener("loadedmetadata", () => {
|
return new Promise((resolve, reject) => {
|
||||||
resolve(element.duration);
|
const element = document.createElement(
|
||||||
element.remove();
|
file.type.startsWith("video/") ? "video" : "audio"
|
||||||
});
|
) as HTMLVideoElement | HTMLAudioElement;
|
||||||
|
|
||||||
element.addEventListener("error", () => {
|
element.addEventListener("loadedmetadata", () => {
|
||||||
reject(new Error("Could not load media"));
|
resolve(element.duration);
|
||||||
element.remove();
|
element.remove();
|
||||||
});
|
});
|
||||||
|
|
||||||
element.src = URL.createObjectURL(file);
|
element.addEventListener("error", () => {
|
||||||
element.load();
|
reject(new Error("Could not load media"));
|
||||||
});
|
element.remove();
|
||||||
};
|
});
|
||||||
|
|
||||||
export const useMediaStore = create<MediaStore>((set, get) => ({
|
element.src = URL.createObjectURL(file);
|
||||||
mediaItems: [],
|
element.load();
|
||||||
isLoading: false,
|
});
|
||||||
|
};
|
||||||
addMediaItem: async (item) => {
|
|
||||||
const newItem: MediaItem = {
|
// Helper to get aspect ratio from MediaItem
|
||||||
...item,
|
export const getMediaAspectRatio = (item: MediaItem): number => {
|
||||||
id: crypto.randomUUID(),
|
if (item.width && item.height) {
|
||||||
};
|
return item.width / item.height;
|
||||||
|
}
|
||||||
// Add to local state immediately for UI responsiveness
|
return 16 / 9; // Default aspect ratio
|
||||||
set((state) => ({
|
};
|
||||||
mediaItems: [...state.mediaItems, newItem],
|
|
||||||
}));
|
export const useMediaStore = create<MediaStore>((set, get) => ({
|
||||||
|
mediaItems: [],
|
||||||
// Save to persistent storage in background
|
isLoading: false,
|
||||||
try {
|
|
||||||
await storageService.saveMediaItem(newItem);
|
addMediaItem: async (item) => {
|
||||||
} catch (error) {
|
const newItem: MediaItem = {
|
||||||
console.error("Failed to save media item:", error);
|
...item,
|
||||||
// Remove from local state if save failed
|
id: crypto.randomUUID(),
|
||||||
set((state) => ({
|
};
|
||||||
mediaItems: state.mediaItems.filter((item) => item.id !== newItem.id),
|
|
||||||
}));
|
// Add to local state immediately for UI responsiveness
|
||||||
}
|
set((state) => ({
|
||||||
},
|
mediaItems: [...state.mediaItems, newItem],
|
||||||
|
}));
|
||||||
removeMediaItem: async (id) => {
|
|
||||||
const state = get();
|
// Save to persistent storage in background
|
||||||
const item = state.mediaItems.find((item) => item.id === id);
|
try {
|
||||||
|
await storageService.saveMediaItem(newItem);
|
||||||
// Cleanup object URLs to prevent memory leaks
|
} catch (error) {
|
||||||
if (item) {
|
console.error("Failed to save media item:", error);
|
||||||
URL.revokeObjectURL(item.url);
|
// Remove from local state if save failed
|
||||||
if (item.thumbnailUrl) {
|
set((state) => ({
|
||||||
URL.revokeObjectURL(item.thumbnailUrl);
|
mediaItems: state.mediaItems.filter((item) => item.id !== newItem.id),
|
||||||
}
|
}));
|
||||||
}
|
}
|
||||||
|
},
|
||||||
// Remove from local state immediately
|
|
||||||
set((state) => ({
|
removeMediaItem: async (id) => {
|
||||||
mediaItems: state.mediaItems.filter((item) => item.id !== id),
|
const state = get();
|
||||||
}));
|
const item = state.mediaItems.find((item) => item.id === id);
|
||||||
|
|
||||||
// Remove from persistent storage
|
// Cleanup object URLs to prevent memory leaks
|
||||||
try {
|
if (item) {
|
||||||
await storageService.deleteMediaItem(id);
|
URL.revokeObjectURL(item.url);
|
||||||
} catch (error) {
|
if (item.thumbnailUrl) {
|
||||||
console.error("Failed to delete media item:", error);
|
URL.revokeObjectURL(item.thumbnailUrl);
|
||||||
// Could re-add to local state here if needed
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
// Remove from local state immediately
|
||||||
loadAllMedia: async () => {
|
set((state) => ({
|
||||||
set({ isLoading: true });
|
mediaItems: state.mediaItems.filter((item) => item.id !== id),
|
||||||
|
}));
|
||||||
try {
|
|
||||||
const mediaItems = await storageService.loadAllMediaItems();
|
// Remove from persistent storage
|
||||||
set({ mediaItems });
|
try {
|
||||||
} catch (error) {
|
await storageService.deleteMediaItem(id);
|
||||||
console.error("Failed to load media items:", error);
|
} catch (error) {
|
||||||
} finally {
|
console.error("Failed to delete media item:", error);
|
||||||
set({ isLoading: false });
|
// Could re-add to local state here if needed
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
clearAllMedia: async () => {
|
loadAllMedia: async () => {
|
||||||
const state = get();
|
set({ isLoading: true });
|
||||||
|
|
||||||
// Cleanup all object URLs
|
try {
|
||||||
state.mediaItems.forEach((item) => {
|
const mediaItems = await storageService.loadAllMediaItems();
|
||||||
URL.revokeObjectURL(item.url);
|
set({ mediaItems });
|
||||||
if (item.thumbnailUrl) {
|
} catch (error) {
|
||||||
URL.revokeObjectURL(item.thumbnailUrl);
|
console.error("Failed to load media items:", error);
|
||||||
}
|
} finally {
|
||||||
});
|
set({ isLoading: false });
|
||||||
|
}
|
||||||
// Clear local state
|
},
|
||||||
set({ mediaItems: [] });
|
|
||||||
|
clearAllMedia: async () => {
|
||||||
// Clear persistent storage
|
const state = get();
|
||||||
try {
|
|
||||||
const mediaIds = state.mediaItems.map((item) => item.id);
|
// Cleanup all object URLs
|
||||||
await Promise.all(
|
state.mediaItems.forEach((item) => {
|
||||||
mediaIds.map((id) => storageService.deleteMediaItem(id))
|
URL.revokeObjectURL(item.url);
|
||||||
);
|
if (item.thumbnailUrl) {
|
||||||
} catch (error) {
|
URL.revokeObjectURL(item.thumbnailUrl);
|
||||||
console.error("Failed to clear media items from storage:", error);
|
}
|
||||||
}
|
});
|
||||||
},
|
|
||||||
}));
|
// Clear local state
|
||||||
|
set({ mediaItems: [] });
|
||||||
|
|
||||||
|
// Clear persistent storage
|
||||||
|
try {
|
||||||
|
const mediaIds = state.mediaItems.map((item) => item.id);
|
||||||
|
await Promise.all(
|
||||||
|
mediaIds.map((id) => storageService.deleteMediaItem(id))
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to clear media items from storage:", error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
@ -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)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user