@animastor/player 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.1 (2026-10-17)
4
+
5
+ BOOK-RECOVER registry gate (stage 59, released via stage 61): the scene
6
+ storyboard read path self-healed PG for any disk book — when no PG durations
7
+ existed it computed them and upserted `image_units` keyed only on disk
8
+ presence, re-creating rows for deleted books on every client poll.
9
+
10
+ ### Fixed
11
+
12
+ - `GET /scene/:b/:ch/:sc/storyboard` — the `image_units` PG upsert
13
+ (duration self-heal) now runs only when the book is still registered
14
+ (owns a `books` row). Durations are still computed and returned in the
15
+ response; only the PG WRITE is suppressed for unregistered books.
16
+
17
+ ### API
18
+
19
+ - No breaking changes. The gate rides on the existing injection seam:
20
+ `playerRoutes(app, redis, deps)` accepts an optional
21
+ `deps.isBookRegistered(bookId) → Promise<boolean>` and threads it into the
22
+ shared contour context (`playerShared.isBookRegistered`). When the host
23
+ does not inject it, behavior is identical to 0.1.0 (fail-open, legacy
24
+ recovery). Root exports unchanged.
25
+
3
26
  ## 0.1.0 — 2026-09-08
4
27
 
5
28
  ### Release preparation (npm publication)
package/README.md CHANGED
@@ -11,9 +11,9 @@ Model and the read-side artifact-naming grammar.
11
11
  **Status: EXTRACTED (0.1.0).** The playback contour physically lives in this
12
12
  package (`src/`); the host backend consumes it only through the package
13
13
  entrypoint. See
14
- [`docs/architecture/PLAYER_ROUTE_SPLIT_CHECKLIST.md`](https://github.com/Animastor/animastor/blob/main/docs/architecture/PLAYER_ROUTE_SPLIT_CHECKLIST.md)
14
+ [`docs/architecture/PLAYER_ROUTE_SPLIT_CHECKLIST.md`](https://github.com/Animastor/animastor-backend/blob/main/docs/architecture/PLAYER_ROUTE_SPLIT_CHECKLIST.md)
15
15
  (physical move COMPLETE) and
16
- [`docs/architecture/PLAYER_PACKAGE_EXTRACTION_READINESS_AUDIT.md`](https://github.com/Animastor/animastor/blob/main/docs/architecture/PLAYER_PACKAGE_EXTRACTION_READINESS_AUDIT.md).
16
+ [`docs/architecture/PLAYER_PACKAGE_EXTRACTION_READINESS_AUDIT.md`](https://github.com/Animastor/animastor-backend/blob/main/docs/architecture/PLAYER_PACKAGE_EXTRACTION_READINESS_AUDIT.md).
17
17
 
18
18
  ## Requirements
19
19
 
@@ -87,8 +87,8 @@ GET /api/v1/preview/:b/:ch/:sc/:iuId GET /api/v1/book/:bookId/chunks
87
87
  GET /api/v1/book/:bookId/assets-state
88
88
  ```
89
89
 
90
- Guards: [`backend/tests/architecture/player-route-split.test.js`](https://github.com/Animastor/animastor/blob/main/backend/tests/architecture/player-route-split.test.js) (P1–P9) and
91
- [`backend/tests/architecture/phase6-editor-player.test.js`](https://github.com/Animastor/animastor/blob/main/backend/tests/architecture/phase6-editor-player.test.js) (T1–T7).
90
+ Guards: [`backend/tests/architecture/player-route-split.test.js`](https://github.com/Animastor/animastor-backend/blob/main/backend/tests/architecture/player-route-split.test.js) (P1–P9) and
91
+ [`backend/tests/architecture/phase6-editor-player.test.js`](https://github.com/Animastor/animastor-backend/blob/main/backend/tests/architecture/phase6-editor-player.test.js) (T1–T7).
92
92
 
93
93
  ## License
94
94
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@animastor/player",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Animastor Player — playback HTTP contour: scene media serving (Range/206/ETag/If-Range), scene data (status/storyboard/timings/waveform), IU media, playback queue and assets-state. Reads book content only through the injected playerModel (VBook boundary); every host dependency arrives as an injected port — no config, no generation, no workflows, no Redis/PG/Redis internals in the package.",
5
5
  "keywords": [
6
6
  "animastor",
@@ -31,12 +31,12 @@
31
31
  },
32
32
  "repository": {
33
33
  "type": "git",
34
- "url": "git+https://github.com/Animastor/animastor.git",
34
+ "url": "git+https://github.com/Animastor/animastor-backend.git",
35
35
  "directory": "packages/animastor-player"
36
36
  },
37
- "homepage": "https://github.com/Animastor/animastor/tree/main/packages/animastor-player#readme",
37
+ "homepage": "https://github.com/Animastor/animastor-backend/tree/main/packages/animastor-player#readme",
38
38
  "bugs": {
39
- "url": "https://github.com/Animastor/animastor/issues"
39
+ "url": "https://github.com/Animastor/animastor-backend/issues"
40
40
  },
41
41
  "dependencies": {
42
42
  "@animastor/vbook-runtime": "^0.1.0"
@@ -51,6 +51,9 @@ module.exports = function(app, redis, deps) {
51
51
  getChunk, getAllChunks, getBookWindowStatus,
52
52
  activeScenes, placeholderAudio, layerConfig, iuRepo,
53
53
  sceneAssetsRepo, playerModel,
54
+ // Book-registry gate (read-time self-heal must not persist for a
55
+ // deleted/unregistered book):
56
+ isBookRegistered,
54
57
  // Player-route seams (composition root provides them):
55
58
  outputRoot, // = config.OUTPUT_DIR (injected)
56
59
  playerPorts, // { assertBookAccess, computeVideoStartMs, computeWaveform }
@@ -73,6 +76,7 @@ module.exports = function(app, redis, deps) {
73
76
  redis,
74
77
  getChunk,
75
78
  iuRepo,
79
+ isBookRegistered,
76
80
  log,
77
81
  });
78
82
 
@@ -37,7 +37,7 @@ const naming = require('./artifact-naming.cjs');
37
37
  * - playerPorts: { assertBookAccess, computeVideoStartMs, computeWaveform }
38
38
  */
39
39
  function createPlayerShared(deps) {
40
- const { playerModel, playerPorts, redis, getChunk, iuRepo, log } = deps;
40
+ const { playerModel, playerPorts, redis, getChunk, iuRepo, isBookRegistered, log } = deps;
41
41
  const outputRoot = deps.outputRoot;
42
42
  const ports = playerPorts || {};
43
43
 
@@ -222,6 +222,7 @@ function createPlayerShared(deps) {
222
222
  get redis() { return redis; },
223
223
  get getChunk() { return getChunk; },
224
224
  get iuRepo() { return iuRepo; },
225
+ get isBookRegistered() { return isBookRegistered; },
225
226
  get outputRoot() { return outputRoot; },
226
227
  get log() { return log; },
227
228
  };
@@ -157,18 +157,28 @@ module.exports = function(app, ctx, deps) {
157
157
  iu._end_ms = endMs;
158
158
  cursorMs = endMs;
159
159
  }
160
- for (const [idx, iu] of ius.entries()) {
161
- try {
162
- await ctx.iuRepo.upsertImageUnit(buildId, bookId, chapterId, sceneId, iu.unit_id, {
163
- scene_order: idx, text: iu.text, text_length: (iu.text || '').length,
164
- text_proportion: iu.text_proportion || 0, scene_duration_sec: sceneDuration || 0,
165
- estimated_duration_sec: iu.estimated_duration_sec || 0,
166
- scene_audio_file: naming.sceneAudioName(bookId, chapterId, sceneId),
167
- start_ms: iu._start_ms != null ? iu._start_ms : null,
168
- end_ms: iu._end_ms != null ? iu._end_ms : null,
169
- });
170
- } catch (pgErr) {
171
- console.warn('[SCENE STORYBOARD] Failed to persist IU to PG:', pgErr.message);
160
+ // Registration gate: only persist the duration self-heal for a
161
+ // book that is still in the registry. A deleted book whose disk
162
+ // book.json remains would otherwise get its image_units rows
163
+ // re-created here every time a client polls the scene storyboard.
164
+ // The in-memory durations are still computed and returned; only
165
+ // the PG write is suppressed.
166
+ const registered = typeof ctx.isBookRegistered !== 'function'
167
+ ? true : await ctx.isBookRegistered(bookId);
168
+ if (registered) {
169
+ for (const [idx, iu] of ius.entries()) {
170
+ try {
171
+ await ctx.iuRepo.upsertImageUnit(buildId, bookId, chapterId, sceneId, iu.unit_id, {
172
+ scene_order: idx, text: iu.text, text_length: (iu.text || '').length,
173
+ text_proportion: iu.text_proportion || 0, scene_duration_sec: sceneDuration || 0,
174
+ estimated_duration_sec: iu.estimated_duration_sec || 0,
175
+ scene_audio_file: naming.sceneAudioName(bookId, chapterId, sceneId),
176
+ start_ms: iu._start_ms != null ? iu._start_ms : null,
177
+ end_ms: iu._end_ms != null ? iu._end_ms : null,
178
+ });
179
+ } catch (pgErr) {
180
+ console.warn('[SCENE STORYBOARD] Failed to persist IU to PG:', pgErr.message);
181
+ }
172
182
  }
173
183
  }
174
184
  }