Replace saved copies that fail to play, reject truncated saves, and re-check the device cache when the app returns to the foreground

This commit is contained in:
Jonathan Sykes
2026-09-18 10:11:20 +08:00
parent 64f45a102d
commit a20dd0a3a6
3 changed files with 141 additions and 7 deletions

View File

@@ -85,6 +85,29 @@
},
// Revoke an object URL previously returned by getFileUrl().
// 'hevc' | 'avc' | null for a stored mp4. Server copies are faststart, so
// the sample description (hvc1/hev1 vs avc1) sits in the first megabyte.
async sniffCodec(videoId) {
try {
const found = await findHandle(videoId);
if (!found) return null;
const file = await found[0].getFile();
const head = new Uint8Array(await file.slice(0, 1024 * 1024).arrayBuffer());
const has = (tag) => {
const [a, b, c, d] = [...tag].map((ch) => ch.charCodeAt(0));
for (let i = 0; i + 3 < head.length; i++) {
if (head[i] === a && head[i + 1] === b && head[i + 2] === c && head[i + 3] === d) return true;
}
return false;
};
if (has('hvc1') || has('hev1')) return 'hevc';
if (has('avc1')) return 'avc';
return null;
} catch {
return null;
}
},
revokeUrl(url) {
if (url && _blobUrls.has(url)) {
URL.revokeObjectURL(url);
@@ -153,6 +176,13 @@
await writable.abort();
throw err;
}
// A connection that ends early can close the body cleanly, leaving
// a truncated file that would be badged "cached" yet never play.
const expected = Number(response.headers.get('content-length'));
if (expected > 0) {
const got = (await tmpHandle.getFile()).size;
if (got !== expected) throw new Error(`download cut short (${got} of ${expected} bytes)`);
}
} else {
// No createWritable on this browser — main-thread OPFS writes are
// impossible (sync access handles are worker-only). Surface a real
@@ -187,7 +217,9 @@
},
// List all cached videos — returns [{ id, size, name }]
async listVideos() {
// strict: rethrow instead of returning a partial list, for callers that
// treat a missing entry as "the file is gone".
async listVideos({ strict = false } = {}) {
const dir = await getRoot();
const items = [];
try {
@@ -200,7 +232,7 @@
const file = await handle.getFile();
items.push({ id, name, size: file.size });
}
} catch { /* OPFS not available */ }
} catch (err) { if (strict) throw err; /* OPFS not available */ }
return items;
},