summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/web/src/components/editor/timeline/timeline-element.tsx12
-rw-r--r--apps/web/src/stores/timeline-store.ts103
2 files changed, 79 insertions, 36 deletions
diff --git a/apps/web/src/components/editor/timeline/timeline-element.tsx b/apps/web/src/components/editor/timeline/timeline-element.tsx
index c66e9fcb..f581bbf0 100644
--- a/apps/web/src/components/editor/timeline/timeline-element.tsx
+++ b/apps/web/src/components/editor/timeline/timeline-element.tsx
@@ -157,16 +157,16 @@ export function TimelineElement({
if (!file) return;
try {
- const success = await replaceElementMedia(track.id, element.id, file);
- if (success) {
+ const result = await replaceElementMedia(track.id, element.id, file);
+ if (result.success) {
toast.success("Clip replaced successfully");
} else {
- toast.error("Failed to replace clip");
+ toast.error(result.error || "Failed to replace clip");
}
} catch (error) {
- toast.error("Failed to replace clip");
- console.log(
- JSON.stringify({ error: "Failed to replace clip", details: error })
+ console.error("Unexpected error replacing clip:", error);
+ toast.error(
+ `Unexpected error: ${error instanceof Error ? error.message : "Unknown error"}`
);
}
};
diff --git a/apps/web/src/stores/timeline-store.ts b/apps/web/src/stores/timeline-store.ts
index 9e3f56d6..f3497be9 100644
--- a/apps/web/src/stores/timeline-store.ts
+++ b/apps/web/src/stores/timeline-store.ts
@@ -150,7 +150,7 @@ interface TimelineStore {
trackId: string,
elementId: string,
newFile: File
- ) => Promise<boolean>;
+ ) => Promise<{ success: boolean; error?: string }>;
// Ripple editing functions
updateElementStartTimeWithRipple: (
@@ -1077,18 +1077,33 @@ export const useTimelineStore = create<TimelineStore>((set, get) => {
},
// Replace media for an element
- replaceElementMedia: async (trackId, elementId, newFile) => {
+ replaceElementMedia: async (
+ trackId: string,
+ elementId: string,
+ newFile: File
+ ): Promise<{ success: boolean; error?: string }> => {
const { _tracks } = get();
const track = _tracks.find((t) => t.id === trackId);
const element = track?.elements.find((c) => c.id === elementId);
- if (!element || element.type !== "media") return false;
+ if (!element) {
+ return { success: false, error: "Timeline element not found" };
+ }
+
+ if (element.type !== "media") {
+ return {
+ success: false,
+ error: "Replace is only available for media clips",
+ };
+ }
try {
const mediaStore = useMediaStore.getState();
const projectStore = useProjectStore.getState();
- if (!projectStore.activeProject) return false;
+ if (!projectStore.activeProject) {
+ return { success: false, error: "No active project found" };
+ }
// Import required media processing functions
const {
@@ -1099,7 +1114,13 @@ export const useTimelineStore = create<TimelineStore>((set, get) => {
} = await import("./media-store");
const fileType = getFileType(newFile);
- if (!fileType) return false;
+ if (!fileType) {
+ return {
+ success: false,
+ error:
+ "Unsupported file type. Please select a video, audio, or image file.",
+ };
+ }
// Process the new media file
const mediaData: any = {
@@ -1109,32 +1130,56 @@ export const useTimelineStore = create<TimelineStore>((set, get) => {
url: URL.createObjectURL(newFile),
};
- // Get media-specific metadata
- if (fileType === "image") {
- const { width, height } = await getImageDimensions(newFile);
- mediaData.width = width;
- mediaData.height = height;
- } else if (fileType === "video") {
- const [duration, { thumbnailUrl, width, height }] = await Promise.all(
- [getMediaDuration(newFile), generateVideoThumbnail(newFile)]
- );
- mediaData.duration = duration;
- mediaData.thumbnailUrl = thumbnailUrl;
- mediaData.width = width;
- mediaData.height = height;
- } else if (fileType === "audio") {
- mediaData.duration = await getMediaDuration(newFile);
+ try {
+ // Get media-specific metadata
+ if (fileType === "image") {
+ const { width, height } = await getImageDimensions(newFile);
+ mediaData.width = width;
+ mediaData.height = height;
+ } else if (fileType === "video") {
+ const [duration, { thumbnailUrl, width, height }] =
+ await Promise.all([
+ getMediaDuration(newFile),
+ generateVideoThumbnail(newFile),
+ ]);
+ mediaData.duration = duration;
+ mediaData.thumbnailUrl = thumbnailUrl;
+ mediaData.width = width;
+ mediaData.height = height;
+ } else if (fileType === "audio") {
+ mediaData.duration = await getMediaDuration(newFile);
+ }
+ } catch (error) {
+ return {
+ success: false,
+ error: `Failed to process ${fileType} file: ${error instanceof Error ? error.message : "Unknown error"}`,
+ };
}
// Add new media item to store
- await mediaStore.addMediaItem(projectStore.activeProject.id, mediaData);
+ try {
+ await mediaStore.addMediaItem(
+ projectStore.activeProject.id,
+ mediaData
+ );
+ } catch (error) {
+ return {
+ success: false,
+ error: `Failed to add media to project: ${error instanceof Error ? error.message : "Unknown error"}`,
+ };
+ }
// Find the newly created media item
const newMediaItem = mediaStore.mediaItems.find(
(item) => item.file === newFile
);
- if (!newMediaItem) return false;
+ if (!newMediaItem) {
+ return {
+ success: false,
+ error: "Failed to create media item in project. Please try again.",
+ };
+ }
get().pushHistory();
@@ -1160,15 +1205,13 @@ export const useTimelineStore = create<TimelineStore>((set, get) => {
)
);
- return true;
+ return { success: true };
} catch (error) {
- console.log(
- JSON.stringify({
- error: "Failed to replace element media",
- details: error,
- })
- );
- return false;
+ console.error("Failed to replace element media:", error);
+ return {
+ success: false,
+ error: `Unexpected error: ${error instanceof Error ? error.message : "Unknown error"}`,
+ };
}
},