refactor: better error handling

This commit is contained in:
Maze Winther
2025-07-26 13:25:10 +02:00
parent 14475dc9a6
commit 906f95399d
2 changed files with 79 additions and 36 deletions
@@ -157,16 +157,16 @@ export function TimelineElement({
if (!file) return; if (!file) return;
try { try {
const success = await replaceElementMedia(track.id, element.id, file); const result = await replaceElementMedia(track.id, element.id, file);
if (success) { if (result.success) {
toast.success("Clip replaced successfully"); toast.success("Clip replaced successfully");
} else { } else {
toast.error("Failed to replace clip"); toast.error(result.error || "Failed to replace clip");
} }
} catch (error) { } catch (error) {
toast.error("Failed to replace clip"); console.error("Unexpected error replacing clip:", error);
console.log( toast.error(
JSON.stringify({ error: "Failed to replace clip", details: error }) `Unexpected error: ${error instanceof Error ? error.message : "Unknown error"}`
); );
} }
}; };
+61 -18
View File
@@ -150,7 +150,7 @@ interface TimelineStore {
trackId: string, trackId: string,
elementId: string, elementId: string,
newFile: File newFile: File
) => Promise<boolean>; ) => Promise<{ success: boolean; error?: string }>;
// Ripple editing functions // Ripple editing functions
updateElementStartTimeWithRipple: ( updateElementStartTimeWithRipple: (
@@ -1077,18 +1077,33 @@ export const useTimelineStore = create<TimelineStore>((set, get) => {
}, },
// Replace media for an element // 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 { _tracks } = get();
const track = _tracks.find((t) => t.id === trackId); const track = _tracks.find((t) => t.id === trackId);
const element = track?.elements.find((c) => c.id === elementId); 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 { try {
const mediaStore = useMediaStore.getState(); const mediaStore = useMediaStore.getState();
const projectStore = useProjectStore.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 // Import required media processing functions
const { const {
@@ -1099,7 +1114,13 @@ export const useTimelineStore = create<TimelineStore>((set, get) => {
} = await import("./media-store"); } = await import("./media-store");
const fileType = getFileType(newFile); 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 // Process the new media file
const mediaData: any = { const mediaData: any = {
@@ -1109,15 +1130,18 @@ export const useTimelineStore = create<TimelineStore>((set, get) => {
url: URL.createObjectURL(newFile), url: URL.createObjectURL(newFile),
}; };
try {
// Get media-specific metadata // Get media-specific metadata
if (fileType === "image") { if (fileType === "image") {
const { width, height } = await getImageDimensions(newFile); const { width, height } = await getImageDimensions(newFile);
mediaData.width = width; mediaData.width = width;
mediaData.height = height; mediaData.height = height;
} else if (fileType === "video") { } else if (fileType === "video") {
const [duration, { thumbnailUrl, width, height }] = await Promise.all( const [duration, { thumbnailUrl, width, height }] =
[getMediaDuration(newFile), generateVideoThumbnail(newFile)] await Promise.all([
); getMediaDuration(newFile),
generateVideoThumbnail(newFile),
]);
mediaData.duration = duration; mediaData.duration = duration;
mediaData.thumbnailUrl = thumbnailUrl; mediaData.thumbnailUrl = thumbnailUrl;
mediaData.width = width; mediaData.width = width;
@@ -1125,16 +1149,37 @@ export const useTimelineStore = create<TimelineStore>((set, get) => {
} else if (fileType === "audio") { } else if (fileType === "audio") {
mediaData.duration = await getMediaDuration(newFile); 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 // 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 // Find the newly created media item
const newMediaItem = mediaStore.mediaItems.find( const newMediaItem = mediaStore.mediaItems.find(
(item) => item.file === newFile (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(); get().pushHistory();
@@ -1160,15 +1205,13 @@ export const useTimelineStore = create<TimelineStore>((set, get) => {
) )
); );
return true; return { success: true };
} catch (error) { } catch (error) {
console.log( console.error("Failed to replace element media:", error);
JSON.stringify({ return {
error: "Failed to replace element media", success: false,
details: error, error: `Unexpected error: ${error instanceof Error ? error.message : "Unknown error"}`,
}) };
);
return false;
} }
}, },