mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 19:04:02 +00:00
fix(director): keep scene results and save status accurate (#971)
- Guard against legacy pack bootstrap during unreadable storage recovery - Report failed saves honestly across captures, updates, and imports - Resolve default scene id correctly when advancing past a specific shot - Count unique scenes executed rather than queued shots in run telemetry - Return run failure status when playback throws instead of false success - Pin five regression scenarios in src/scenes/director971.test.mjs
This commit is contained in:
1 parent
3ec31e599e
commit
2b6e45e3ef
3 files changed
+247
-29
No files matched your search
+36
-29
@@ -183,20 +183,22 @@ export class SceneDirector {
|
||||
...this._presentation,
|
||||
hasRun: !!this._lastRunJson,
|
||||
}));
|
||||
this._bootstrapLegacyShotPacks();
|
||||
// Upgrade only already-installed packs; unrelated Scenes are untouched.
|
||||
for (const scene of this._project.scenes) {
|
||||
for (const marker of [...(scene.appliedShotPacks || [])]) {
|
||||
const recipe = getSceneAppendRecipeById(marker.id);
|
||||
if (
|
||||
recipe?.expansionFromVersion &&
|
||||
marker.version > 0 &&
|
||||
marker.version <= recipe.expansionFromVersion
|
||||
) {
|
||||
this.appendShotPack(scene.id, marker.id, {
|
||||
render: false,
|
||||
announce: false,
|
||||
});
|
||||
if (!this._storageReadError) {
|
||||
this._bootstrapLegacyShotPacks();
|
||||
// Upgrade only already-installed packs; unrelated Scenes are untouched.
|
||||
for (const scene of this._project.scenes) {
|
||||
for (const marker of [...(scene.appliedShotPacks || [])]) {
|
||||
const recipe = getSceneAppendRecipeById(marker.id);
|
||||
if (
|
||||
recipe?.expansionFromVersion &&
|
||||
marker.version > 0 &&
|
||||
marker.version <= recipe.expansionFromVersion
|
||||
) {
|
||||
this.appendShotPack(scene.id, marker.id, {
|
||||
render: false,
|
||||
announce: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -316,13 +318,14 @@ export class SceneDirector {
|
||||
this._toastStorageError(
|
||||
'Scene not saved — existing saved project could not be read. Export edits or import a valid file.',
|
||||
);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
this._project.updatedAt = new Date().toISOString();
|
||||
try {
|
||||
const payload = JSON.stringify(this._project);
|
||||
parseSceneDocument(payload);
|
||||
localStorage.setItem(STORAGE_KEY, payload);
|
||||
return true;
|
||||
} catch (e) {
|
||||
// Private browsing / block-all-cookies / quota-exceeded throws here. The
|
||||
// in-memory project stays usable this session, but persistence failed —
|
||||
@@ -336,6 +339,7 @@ export class SceneDirector {
|
||||
? `Scene not saved — ${e.message}`
|
||||
: undefined,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -889,12 +893,12 @@ export class SceneDirector {
|
||||
}
|
||||
this._selectedSceneId = scene.id;
|
||||
this._selectedShotId = appendedShots[0]?.id || this._selectedShotId;
|
||||
this._saveProject();
|
||||
const saved = this._saveProject();
|
||||
if (render) {
|
||||
this._renderSceneSelect();
|
||||
this._renderShotList();
|
||||
}
|
||||
if (announce) {
|
||||
if (announce && saved) {
|
||||
this._updateStatus(
|
||||
marker
|
||||
? `Updated ${patchedShotCount} ${patchedShotCount === 1 ? 'shot' : 'shots'}: ${recipe.title}`
|
||||
@@ -939,9 +943,9 @@ export class SceneDirector {
|
||||
|
||||
scene.shots.push(shot);
|
||||
this._selectedShotId = shot.id;
|
||||
this._saveProject();
|
||||
const saved = this._saveProject();
|
||||
this._shotOutcome('shot-captured', scene, shot);
|
||||
this._updateStatus(`Captured: ${scene.title} / ${shot.title}`);
|
||||
if (saved) this._updateStatus(`Captured: ${scene.title} / ${shot.title}`);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -967,9 +971,9 @@ export class SceneDirector {
|
||||
shot.visual = this.styleManager.getVisualState();
|
||||
shot.layers = this._captureLayerStates();
|
||||
|
||||
this._saveProject();
|
||||
const saved = this._saveProject();
|
||||
this._shotOutcome('shot-updated', scene, shot);
|
||||
this._updateStatus(`Updated: ${scene.title} / ${shot.title}`);
|
||||
if (saved) this._updateStatus(`Updated: ${scene.title} / ${shot.title}`);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1686,13 +1690,13 @@ export class SceneDirector {
|
||||
) {
|
||||
if (this._running) return { started: false, reason: 'already-running' };
|
||||
|
||||
let queue = this._buildPlaybackQueue(
|
||||
sceneId || this._selectedSceneId || this._project.scenes[0]?.id,
|
||||
{ single },
|
||||
);
|
||||
const startSceneId =
|
||||
sceneId || this._selectedSceneId || this._project.scenes[0]?.id;
|
||||
let queue = this._buildPlaybackQueue(startSceneId, { single });
|
||||
if (afterShotId !== null) {
|
||||
const index = queue.findIndex(
|
||||
({ scene, shot }) => scene.id === sceneId && shot.id === afterShotId,
|
||||
({ scene, shot }) =>
|
||||
scene.id === startSceneId && shot.id === afterShotId,
|
||||
);
|
||||
if (index < 0) return { started: false, reason: 'shot-not-found' };
|
||||
queue = queue.slice(index + 1);
|
||||
@@ -1764,7 +1768,7 @@ export class SceneDirector {
|
||||
title: 'Editable Scene Run',
|
||||
startedAt: new Date().toISOString(),
|
||||
estimatedDurationSec,
|
||||
scenesRun: queue.length,
|
||||
scenesRun: new Set(queue.map(({ scene }) => scene.id)).size,
|
||||
events: [],
|
||||
};
|
||||
|
||||
@@ -1772,6 +1776,7 @@ export class SceneDirector {
|
||||
this._logEvent('scene_run_start', { count: queue.length });
|
||||
this._setPlaybackKeyboardEnabled(true);
|
||||
|
||||
let runResult = { started: true, shots: queue.length };
|
||||
try {
|
||||
await playSceneQueue(queue, {
|
||||
token,
|
||||
@@ -1786,9 +1791,11 @@ export class SceneDirector {
|
||||
this._logEvent('scene_run_error', {
|
||||
message: error.message || 'unknown error',
|
||||
});
|
||||
runResult = { started: false, reason: 'run-failed' };
|
||||
} finally {
|
||||
this._finishRun();
|
||||
}
|
||||
return runResult;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2014,9 +2021,9 @@ export class SceneDirector {
|
||||
this._selectedShotId =
|
||||
selection?.shotId || project.scenes[0]?.shots[0]?.id || null;
|
||||
this._loadedSceneId = null;
|
||||
this._saveProject();
|
||||
const saved = this._saveProject();
|
||||
this._publish({ type: 'project-imported', project });
|
||||
this._updateStatus(`Imported ${file.name}`);
|
||||
if (saved) this._updateStatus(`Imported ${file.name}`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
if (
|
||||
|
||||
Reference in new issue
Block a user