openvibers/openvibe.media Commits
Reconcile: a virtual clip needs no location of its own (eleven timeline clips were listed as no_canonical_location) (#147)
Changes
@@@ -8,7 +8,7 @@* (state present/missing/corrupt, size, verified_at). Never deletes anything.** Detects:-- * no_canonical_location ready object with no row for its canonical copy++ * no_canonical_location ready object with no row for its canonical copy (a virtual clip needs none)* canonical_missing_replica_present canonical copy gone/corrupt while another copy is present* no_present_copy ready object with no present copy anywhere* missing_local_file ready object's local location whose file is not on disk@@@ -144,6 +144,9 @@ async function reconcile({ verify = false, hash = false, head = defaultHead, has// Per object: is the canonical copy there, and is any copy there at all?for (const obj of objects) {if (obj.lifecycle_status !== 'ready') continue;++ // A virtual clip (F3.5/F3.6) plays its window of the recording's timeline: no location of its own is needed, as++ // readiness and the verify job already read it. Eleven ready ones were listed as no_canonical_location.++ if (model.parseJson(obj.metadata, {}).virtual) continue;const locs = byObject.get(obj.id) || [];const canon = obj.canonical_provider ? locs.find(l => l.provider === obj.canonical_provider) : null;const present = locs.filter(l => effective.get(l.id) === 'present');@@@ -61,6 +61,7 @@ const crypto = require('crypto');await db.run("UPDATE media_objects SET lifecycle_status = 'deleted' WHERE id = ?", [await objId(2)]); // deleted, but /v/2 still serves itawait d.prepare("INSERT INTO clips (id, app_id, file_path, status) OVERRIDING SYSTEM VALUE VALUES (9, 'live', '/x.webm', 'ready') RETURNING id").run(); // row with no objectconst ready = await model.createObject({ app_id: 'live', kind: 'file', lifecycle_status: 'ready' }); // ready object, no location++ await model.createObject({ app_id: 'live', kind: 'clip', lifecycle_status: 'ready', metadata: { virtual: true } }); // a virtual clip: needs none// Fake provider: key → size (absent = 404). B2 lost c.webm (R2 still has it) and holds a wrong-size d.webm.const bucket = { b2: { 'vods/b.webm': 200, 'vods/d.webm': 12345 }, r2: { 'vods/c.webm': 300 } };@@@ -83,7 +84,7 @@ const crypto = require('crypto');assert.strictEqual(ro.issues.deleted_publicly_reachable.items[0].table, 'vods');assert.strictEqual(count(ro, 'missing_projection'), 1);assert.deepStrictEqual(ro.issues.missing_projection.items[0], { table: 'clips', id: 9 });-- assert.strictEqual(count(ro, 'no_canonical_location'), 1);++ assert.strictEqual(count(ro, 'no_canonical_location'), 1, 'the virtual clip plays from its recording\'s timeline: not listed');assert.strictEqual(ro.issues.no_canonical_location.items[0].object_id, ready);assert.strictEqual(count(ro, 'canonical_missing_replica_present'), 0, 'remote copies are unverified until --verify');assert.strictEqual(ro.counts.remote_unverified, 4, 'b2 ×3 + r2 ×1 pending');
@@ -8,7 +8,7 @@
@@ -8,7 +8,7 @@
* (state present/missing/corrupt, size, verified_at). Never deletes anything.
* (state present/missing/corrupt, size, verified_at). Never deletes anything.
*
*
* Detects:
* Detects:
- * no_canonical_location ready object with no row for its canonical copy
+ * no_canonical_location ready object with no row for its canonical copy (a virtual clip needs none)
* canonical_missing_replica_present canonical copy gone/corrupt while another copy is present
* canonical_missing_replica_present canonical copy gone/corrupt while another copy is present
* no_present_copy ready object with no present copy anywhere
* no_present_copy ready object with no present copy anywhere
* missing_local_file ready object's local location whose file is not on disk
* missing_local_file ready object's local location whose file is not on disk
@@ -144,6 +144,9 @@ async function reconcile({ verify = false, hash = false, head = defaultHead, has
@@ -144,6 +144,9 @@ async function reconcile({ verify = false, hash = false, head = defaultHead, has
// Per object: is the canonical copy there, and is any copy there at all?
// Per object: is the canonical copy there, and is any copy there at all?
for (const obj of objects) {
for (const obj of objects) {
if (obj.lifecycle_status !== 'ready') continue;
if (obj.lifecycle_status !== 'ready') continue;
+ // A virtual clip (F3.5/F3.6) plays its window of the recording's timeline: no location of its own is needed, as
+ // readiness and the verify job already read it. Eleven ready ones were listed as no_canonical_location.
+ if (model.parseJson(obj.metadata, {}).virtual) continue;
const locs = byObject.get(obj.id) || [];
const locs = byObject.get(obj.id) || [];
const canon = obj.canonical_provider ? locs.find(l => l.provider === obj.canonical_provider) : null;
const canon = obj.canonical_provider ? locs.find(l => l.provider === obj.canonical_provider) : null;
const present = locs.filter(l => effective.get(l.id) === 'present');
const present = locs.filter(l => effective.get(l.id) === 'present');
@@ -61,6 +61,7 @@ const crypto = require('crypto');
@@ -61,6 +61,7 @@ const crypto = require('crypto');
await db.run("UPDATE media_objects SET lifecycle_status = 'deleted' WHERE id = ?", [await objId(2)]); // deleted, but /v/2 still serves it
await db.run("UPDATE media_objects SET lifecycle_status = 'deleted' WHERE id = ?", [await objId(2)]); // deleted, but /v/2 still serves it
await d.prepare("INSERT INTO clips (id, app_id, file_path, status) OVERRIDING SYSTEM VALUE VALUES (9, 'live', '/x.webm', 'ready') RETURNING id").run(); // row with no object
await d.prepare("INSERT INTO clips (id, app_id, file_path, status) OVERRIDING SYSTEM VALUE VALUES (9, 'live', '/x.webm', 'ready') RETURNING id").run(); // row with no object
const ready = await model.createObject({ app_id: 'live', kind: 'file', lifecycle_status: 'ready' }); // ready object, no location
const ready = await model.createObject({ app_id: 'live', kind: 'file', lifecycle_status: 'ready' }); // ready object, no location
+ await model.createObject({ app_id: 'live', kind: 'clip', lifecycle_status: 'ready', metadata: { virtual: true } }); // a virtual clip: needs none
// Fake provider: key → size (absent = 404). B2 lost c.webm (R2 still has it) and holds a wrong-size d.webm.
// Fake provider: key → size (absent = 404). B2 lost c.webm (R2 still has it) and holds a wrong-size d.webm.
const bucket = { b2: { 'vods/b.webm': 200, 'vods/d.webm': 12345 }, r2: { 'vods/c.webm': 300 } };
const bucket = { b2: { 'vods/b.webm': 200, 'vods/d.webm': 12345 }, r2: { 'vods/c.webm': 300 } };
@@ -83,7 +84,7 @@ const crypto = require('crypto');
@@ -83,7 +84,7 @@ const crypto = require('crypto');
assert.strictEqual(ro.issues.deleted_publicly_reachable.items[0].table, 'vods');
assert.strictEqual(ro.issues.deleted_publicly_reachable.items[0].table, 'vods');
assert.strictEqual(count(ro, 'missing_projection'), 1);
assert.strictEqual(count(ro, 'missing_projection'), 1);
assert.deepStrictEqual(ro.issues.missing_projection.items[0], { table: 'clips', id: 9 });
assert.deepStrictEqual(ro.issues.missing_projection.items[0], { table: 'clips', id: 9 });
- assert.strictEqual(count(ro, 'no_canonical_location'), 1);
+ assert.strictEqual(count(ro, 'no_canonical_location'), 1, 'the virtual clip plays from its recording\'s timeline: not listed');
assert.strictEqual(ro.issues.no_canonical_location.items[0].object_id, ready);
assert.strictEqual(ro.issues.no_canonical_location.items[0].object_id, ready);
assert.strictEqual(count(ro, 'canonical_missing_replica_present'), 0, 'remote copies are unverified until --verify');
assert.strictEqual(count(ro, 'canonical_missing_replica_present'), 0, 'remote copies are unverified until --verify');
assert.strictEqual(ro.counts.remote_unverified, 4, 'b2 ×3 + r2 ×1 pending');
assert.strictEqual(ro.counts.remote_unverified, 4, 'b2 ×3 + r2 ×1 pending');