WIP: Phase 3B playback engine handoff
This commit is contained in:
@@ -4,6 +4,7 @@ const crypto = require("node:crypto");
|
||||
const { openDatabase } = require("../src/database");
|
||||
const { runMigrations } = require("../src/database/migrate");
|
||||
const { createAssetService } = require("../src/assets/service");
|
||||
const { createPlaybackService } = require("../src/playback");
|
||||
const {
|
||||
MAX_DURATION_MS,
|
||||
MIN_DURATION_MS,
|
||||
@@ -19,13 +20,22 @@ function createHarness() {
|
||||
let idCounter = 0;
|
||||
let assetCounter = 0;
|
||||
const changes = [];
|
||||
let playbackService;
|
||||
const service = createPlaylistService({
|
||||
database,
|
||||
now: () => clock,
|
||||
createId: () =>
|
||||
`00000000-0000-4000-8000-${String(++idCounter).padStart(12, "0")}`,
|
||||
onChanged: (change) => changes.push(change),
|
||||
onBeforeChange: () => {
|
||||
playbackService?.reconcile("before_domain_change");
|
||||
},
|
||||
onChanged: (change) => {
|
||||
changes.push(change);
|
||||
playbackService?.domainChanged(change);
|
||||
},
|
||||
});
|
||||
playbackService = createPlaybackService({ database, now: () => clock });
|
||||
playbackService.initialize();
|
||||
|
||||
function setNow(value) {
|
||||
clock = value;
|
||||
@@ -71,7 +81,15 @@ function createHarness() {
|
||||
.get();
|
||||
}
|
||||
|
||||
return { changes, database, playback, seedAsset, service, setNow };
|
||||
return {
|
||||
changes,
|
||||
database,
|
||||
playback,
|
||||
playbackService,
|
||||
seedAsset,
|
||||
service,
|
||||
setNow,
|
||||
};
|
||||
}
|
||||
|
||||
function expectError(action, code, statusCode) {
|
||||
@@ -479,7 +497,7 @@ test("activation queues at a live boundary, is idempotent, and mode off is only
|
||||
durationOverrideMs: 4000,
|
||||
});
|
||||
|
||||
const initial = harness.service.activate(oldPlaylist.id);
|
||||
const initial = harness.playbackService.activatePlaylist(oldPlaylist.id);
|
||||
assert.equal(initial.activation, "active");
|
||||
assert.equal(initial.playback.currentEntryId, oldEntry.id);
|
||||
assert.equal(initial.playback.slideStartedAt, BASE_TIME);
|
||||
@@ -489,7 +507,7 @@ test("activation queues at a live boundary, is idempotent, and mode off is only
|
||||
.prepare("UPDATE playback_state SET mode = 'off' WHERE output_id = 'global'")
|
||||
.run();
|
||||
harness.setNow(BASE_TIME + 1000);
|
||||
const pending = harness.service.activate(newPlaylist.id);
|
||||
const pending = harness.playbackService.activatePlaylist(newPlaylist.id);
|
||||
assert.equal(pending.activation, "pending");
|
||||
assert.equal(pending.playback.mode, "off");
|
||||
assert.equal(pending.playback.activePlaylistId, oldPlaylist.id);
|
||||
@@ -498,18 +516,18 @@ test("activation queues at a live boundary, is idempotent, and mode off is only
|
||||
const pendingRevision = pending.playback.revision;
|
||||
const requestedAt = pending.playback.pendingPlaylistRequestedAt;
|
||||
|
||||
const repeated = harness.service.activate(newPlaylist.id);
|
||||
const repeated = harness.playbackService.activatePlaylist(newPlaylist.id);
|
||||
assert.equal(repeated.activation, "pending");
|
||||
assert.equal(repeated.playback.revision, pendingRevision);
|
||||
assert.equal(repeated.playback.pendingPlaylistRequestedAt, requestedAt);
|
||||
|
||||
const cancel = harness.service.activate(oldPlaylist.id);
|
||||
const cancel = harness.playbackService.activatePlaylist(oldPlaylist.id);
|
||||
assert.equal(cancel.activation, "already_active");
|
||||
assert.equal(cancel.playback.pendingPlaylistId, null);
|
||||
assert.equal(cancel.playback.currentEntryId, oldEntry.id);
|
||||
|
||||
harness.setNow(BASE_TIME + 7000);
|
||||
const immediate = harness.service.activate(newPlaylist.id);
|
||||
const immediate = harness.playbackService.activatePlaylist(newPlaylist.id);
|
||||
assert.equal(immediate.activation, "active");
|
||||
assert.equal(immediate.playback.activePlaylistId, newPlaylist.id);
|
||||
assert.equal(immediate.playback.pendingPlaylistId, null);
|
||||
@@ -534,7 +552,7 @@ test("paused normal playback queues and can replace a pending activation", (t) =
|
||||
harness.service.addEntry(secondTarget.id, {
|
||||
assetId: harness.seedAsset({ id: "second-target", name: "Second target" }),
|
||||
});
|
||||
harness.service.activate(current.id);
|
||||
harness.playbackService.activatePlaylist(current.id);
|
||||
harness.database
|
||||
.prepare(
|
||||
`UPDATE playback_state
|
||||
@@ -545,14 +563,14 @@ test("paused normal playback queues and can replace a pending activation", (t) =
|
||||
.run();
|
||||
|
||||
harness.setNow(BASE_TIME + 1000);
|
||||
const first = harness.service.activate(firstTarget.id);
|
||||
const first = harness.playbackService.activatePlaylist(firstTarget.id);
|
||||
assert.equal(first.activation, "pending");
|
||||
assert.equal(first.playback.activePlaylistId, current.id);
|
||||
assert.equal(first.playback.pendingPlaylistId, firstTarget.id);
|
||||
assert.equal(first.playback.pausedRemainingMs, 5000);
|
||||
|
||||
harness.setNow(BASE_TIME + 2000);
|
||||
const replacement = harness.service.activate(secondTarget.id);
|
||||
const replacement = harness.playbackService.activatePlaylist(secondTarget.id);
|
||||
assert.equal(replacement.activation, "pending");
|
||||
assert.equal(replacement.playback.activePlaylistId, current.id);
|
||||
assert.equal(replacement.playback.pendingPlaylistId, secondTarget.id);
|
||||
@@ -579,7 +597,7 @@ test("static and paused activation prepare the first playable normal target with
|
||||
durationOverrideMs: 9000,
|
||||
});
|
||||
assert.equal(skipped.position, 0);
|
||||
harness.service.activate(oldPlaylist.id);
|
||||
harness.playbackService.activatePlaylist(oldPlaylist.id);
|
||||
harness.database
|
||||
.prepare(
|
||||
`UPDATE playback_state
|
||||
@@ -590,7 +608,7 @@ test("static and paused activation prepare the first playable normal target with
|
||||
)
|
||||
.run(staticAssetId);
|
||||
|
||||
const result = harness.service.activate(target.id);
|
||||
const result = harness.playbackService.activatePlaylist(target.id);
|
||||
assert.equal(result.activation, "active");
|
||||
assert.equal(result.playback.activePlaylistId, target.id);
|
||||
assert.equal(result.playback.staticAssetId, staticAssetId);
|
||||
@@ -611,7 +629,7 @@ test("empty activation is immediate and current-entry deletion coordinates playb
|
||||
assetId: harness.seedAsset({ id: `asset-${id}`, name: id }),
|
||||
}),
|
||||
);
|
||||
harness.service.activate(populated.id);
|
||||
harness.playbackService.activatePlaylist(populated.id);
|
||||
const futurePlaybackTimestamp = BASE_TIME + 100_000;
|
||||
harness.database
|
||||
.prepare(
|
||||
@@ -624,10 +642,10 @@ test("empty activation is immediate and current-entry deletion coordinates playb
|
||||
|
||||
harness.service.removeEntry(populated.id, entries[0].id);
|
||||
const afterDelete = harness.playback();
|
||||
assert.equal(afterDelete.current_entry_id, null);
|
||||
assert.equal(afterDelete.current_entry_id, entries[1].id);
|
||||
assert.equal(afterDelete.slide_started_at, null);
|
||||
assert.equal(afterDelete.next_slide_at, null);
|
||||
assert.equal(afterDelete.paused_remaining_ms, null);
|
||||
assert.equal(afterDelete.paused_remaining_ms, 7000);
|
||||
assert.equal(afterDelete.active_playlist_id, populated.id);
|
||||
assert.equal(afterDelete.mode, "off");
|
||||
assert.equal(afterDelete.static_asset_id, "asset-second");
|
||||
@@ -646,7 +664,7 @@ test("empty activation is immediate and current-entry deletion coordinates playb
|
||||
WHERE output_id = 'global'`,
|
||||
)
|
||||
.run();
|
||||
const result = harness.service.activate(empty.id);
|
||||
const result = harness.playbackService.activatePlaylist(empty.id);
|
||||
assert.equal(result.activation, "active");
|
||||
assert.equal(result.playback.activePlaylistId, empty.id);
|
||||
assert.equal(result.playback.currentEntryId, null);
|
||||
@@ -665,7 +683,7 @@ test("inactive playlist edits do not mutate authoritative playback state", (t) =
|
||||
const draftEntry = harness.service.addEntry(inactive.id, {
|
||||
assetId: harness.seedAsset({ id: "draft", name: "Draft asset" }),
|
||||
});
|
||||
harness.service.activate(active.id);
|
||||
harness.playbackService.activatePlaylist(active.id);
|
||||
const before = harness.playback();
|
||||
|
||||
harness.service.update(inactive.id, { name: "Draft edited" });
|
||||
|
||||
Reference in New Issue
Block a user