fix(project): surface loading failures and validate media sessions

This commit is contained in:
webadderall
2026-09-20 21:29:53 +10:00
parent 1f729022e4
commit 2f905893e3
2 changed files with 82 additions and 63 deletions
@@ -104,11 +104,10 @@ export function useProjectLifecycle(input: Input) {
current.setCurrentTime(0);
current.setDuration(0);
project.setError(null);
project.setVideoSourcePath(sourcePath);
project.setCurrentProjectPath(path ?? null);
refs.pendingFreshRecordingAutoZoomPathRef.current = null;
if (editor.webcam.sourcePath) {
await window.electronAPI.setCurrentRecordingSession?.(
const result = await window.electronAPI.setCurrentRecordingSession?.(
{
videoPath: sourcePath,
webcamPath: editor.webcam.sourcePath,
@@ -116,15 +115,20 @@ export function useProjectLifecycle(input: Input) {
},
{ preserveProjectPath: Boolean(path) },
);
if (result && !result.success) throw new Error("Could not load project media");
const session = await window.electronAPI.getCurrentRecordingSession?.();
current.applySessionPresentation(session?.success ? session.session : null);
} else {
await window.electronAPI.setCurrentVideoPath(sourcePath, {
const result = await window.electronAPI.setCurrentVideoPath(sourcePath, {
preserveProjectPath: Boolean(path),
});
if (!result.success) throw new Error("Could not load project media");
current.applySessionPresentation(null);
}
project.setVideoPath(await resolveVideoUrl(sourcePath));
const videoUrl = await resolveVideoUrl(sourcePath);
project.setVideoSourcePath(sourcePath);
project.setCurrentProjectPath(path ?? null);
project.setVideoPath(videoUrl);
appearance.setWallpaper(editor.wallpaper);
appearance.setShadowIntensity(editor.shadowIntensity);
@@ -66,19 +66,25 @@ export function useProjectOpenActions({
const handleOpenProjectFromLibrary = useCallback(
async (projectPath: string) => {
if (!(await confirmReplaceSourceWithUnsavedChanges("open another project"))) return;
const result = await window.electronAPI.openProjectFileAtPath(projectPath);
if (result.canceled) return;
if (!result.success) {
toast.error(result.message || "Failed to load project");
return;
try {
const result = await window.electronAPI.openProjectFileAtPath(projectPath);
if (result.canceled) return;
if (!result.success) {
project.setError(result.error || result.message || "Failed to load project");
return;
}
if (!(await applyLoadedProject(result.project, result.path ?? null))) {
project.setError("Could not load project: invalid project file format");
return;
}
project.setProjectBrowserOpen(false);
await refreshProjectLibrary();
toast.success(`Project loaded from ${result.path}`);
} catch (error) {
project.setError(
`Could not load project: ${error instanceof Error ? error.message : String(error)}`,
);
}
if (!(await applyLoadedProject(result.project, result.path ?? null))) {
toast.error("Invalid project file format");
return;
}
project.setProjectBrowserOpen(false);
await refreshProjectLibrary();
toast.success(`Project loaded from ${result.path}`);
},
[
applyLoadedProject,
@@ -90,57 +96,66 @@ export function useProjectOpenActions({
const handleImportMediaOrProject = useCallback(async () => {
if (!(await confirmReplaceSourceWithUnsavedChanges("import a file"))) return;
const result = await window.electronAPI.openVideoFilePicker({ includeProjects: true });
if (result.canceled) return;
if (!result.success) {
toast.error(result.message || "Failed to import file");
return;
}
if (result.kind === "project" || result.project) {
if (!(await applyLoadedProject(result.project, result.path ?? null))) {
toast.error("Invalid project file format");
try {
const result = await window.electronAPI.openVideoFilePicker({ includeProjects: true });
if (result.canceled) return;
if (!result.success) {
toast.error(result.message || "Failed to import file");
return;
}
if (result.kind === "project" || result.project) {
if (!(await applyLoadedProject(result.project, result.path ?? null))) {
project.setError("Could not load project: invalid project file format");
return;
}
project.setProjectBrowserOpen(false);
await refreshProjectLibrary();
toast.success(
result.path ? `Project loaded from ${result.path}` : "Project loaded",
);
return;
}
if (!result.path) {
toast.error("No media file selected");
return;
}
const sourcePath = fromFileUrl(result.path);
await window.electronAPI.setCurrentVideoPath(sourcePath, {
preserveProjectPath: false,
});
const sourceVideoUrl = await resolveVideoUrl(sourcePath);
try {
videoPlaybackRef.current?.pause();
} catch {
// The preview may already be tearing down.
}
setIsPlaying(false);
setCurrentTime(0);
setDuration(0);
project.setVideoSourcePath(sourcePath);
project.setVideoPath(sourceVideoUrl);
project.setCurrentProjectPath(null);
project.setLastSavedSnapshot(null);
resetSourceScopedEditorState();
pendingFreshRecordingAutoZoomPathRef.current =
appearance.autoApplyFreshRecordingAutoZooms ? sourceVideoUrl : null;
appearance.setWebcam((previous) => ({
...previous,
visibleRanges: undefined,
enabled: false,
sourcePath: null,
timeOffsetMs: DEFAULT_WEBCAM_TIME_OFFSET_MS,
}));
applySessionPresentation(null);
project.setProjectBrowserOpen(false);
await refreshProjectLibrary();
toast.success(result.path ? `Project loaded from ${result.path}` : "Project loaded");
return;
toast.success("Media imported");
} catch (error) {
project.setError(
`Could not load file: ${error instanceof Error ? error.message : String(error)}`,
);
}
if (!result.path) {
toast.error("No media file selected");
return;
}
const sourcePath = fromFileUrl(result.path);
await window.electronAPI.setCurrentVideoPath(sourcePath, { preserveProjectPath: false });
const sourceVideoUrl = await resolveVideoUrl(sourcePath);
try {
videoPlaybackRef.current?.pause();
} catch {
// The preview may already be tearing down.
}
setIsPlaying(false);
setCurrentTime(0);
setDuration(0);
project.setVideoSourcePath(sourcePath);
project.setVideoPath(sourceVideoUrl);
project.setCurrentProjectPath(null);
project.setLastSavedSnapshot(null);
resetSourceScopedEditorState();
pendingFreshRecordingAutoZoomPathRef.current = appearance.autoApplyFreshRecordingAutoZooms
? sourceVideoUrl
: null;
appearance.setWebcam((previous) => ({
...previous,
visibleRanges: undefined,
enabled: false,
sourcePath: null,
timeOffsetMs: DEFAULT_WEBCAM_TIME_OFFSET_MS,
}));
applySessionPresentation(null);
project.setProjectBrowserOpen(false);
await refreshProjectLibrary();
toast.success("Media imported");
}, [
confirmReplaceSourceWithUnsavedChanges,
applyLoadedProject,