Extend JamesDSP with convolution and spatial audio
This commit is contained in:
@@ -3,12 +3,22 @@
|
||||
'use strict';
|
||||
const CACHE = 'ytplayer-assets';
|
||||
const STATE = '/__ytp_asset_state';
|
||||
const OPT_INS = '/__ytp_offline_opt_ins';
|
||||
const url = (path, file) => path + '?v=' + file.h;
|
||||
|
||||
function paths(manifest) {
|
||||
return Object.keys(manifest.files).sort();
|
||||
}
|
||||
|
||||
function offlinePaths(manifest, optIns = []) {
|
||||
const allowed = new Set(optIns || []), optional = new Map();
|
||||
for (const group of Object.values(manifest.groups || {})) {
|
||||
if (!group.offlineOptIn || allowed.has(group.offlineOptIn)) continue;
|
||||
for (const path of group.files || []) optional.set(path, true);
|
||||
}
|
||||
return paths(manifest).filter(path => !optional.has(path));
|
||||
}
|
||||
|
||||
function blocking(manifest, previous, activeLayout) {
|
||||
const names = new Set(Object.entries(manifest.groups)
|
||||
.filter(([name, group]) => name === 'core' || !group.background ||
|
||||
@@ -61,13 +71,17 @@
|
||||
return valid;
|
||||
}
|
||||
|
||||
async function status(manifest, cache, { previous, activeLayout, offline = true } = {}) {
|
||||
async function readOptIns(cache) {
|
||||
try { return (await (await cache.match(OPT_INS))?.json()) || []; } catch { return []; }
|
||||
}
|
||||
|
||||
async function status(manifest, cache, { previous, activeLayout, offline = true, optIns } = {}) {
|
||||
const found = await Promise.all(blocking(manifest, previous, activeLayout).map(async path => {
|
||||
const response = await cache.match(url(path, manifest.files[path]));
|
||||
return verifiedProbe(response, manifest.files[path].h);
|
||||
}));
|
||||
const missing = found.filter(value => !value).length;
|
||||
return { ready: missing === 0, missing, version: manifest.buildTag, ...(offline ? await completeness(manifest, cache) : {}) };
|
||||
return { ready: missing === 0, missing, version: manifest.buildTag, ...(offline ? await completeness(manifest, cache, { optIns }) : {}) };
|
||||
}
|
||||
|
||||
// All concurrent messages/layout jobs in this worker share six download slots.
|
||||
@@ -89,10 +103,10 @@
|
||||
finally { pendingDownloads.delete(key); }
|
||||
}
|
||||
|
||||
async function syncAssets(manifest, { cache, fetchFn, concurrency = 6, attempts = 3, activeLayout, groups, all = false }) {
|
||||
async function syncAssets(manifest, { cache, fetchFn, concurrency = 6, attempts = 3, activeLayout, groups, all = false, optIns } = {}) {
|
||||
const previous = (await state(cache))?.current;
|
||||
const missing = [];
|
||||
const selected = all ? paths(manifest) : groups ? [...new Set(groups.flatMap(name => manifest.groups[name]?.files || []))].sort()
|
||||
const selected = all ? offlinePaths(manifest, optIns || await readOptIns(cache)) : groups ? [...new Set(groups.flatMap(name => manifest.groups[name]?.files || []))].sort()
|
||||
: blocking(manifest, previous, activeLayout);
|
||||
for (const path of selected) {
|
||||
const key = url(path, manifest.files[path]);
|
||||
@@ -135,8 +149,8 @@
|
||||
|
||||
// Verified cache entries are the durable progress journal. Counters are rebuilt
|
||||
// after worker termination or browser eviction; never trust a stored ready bit.
|
||||
async function completeness(manifest, cache) {
|
||||
const files = paths(manifest);
|
||||
async function completeness(manifest, cache, { optIns } = {}) {
|
||||
const files = offlinePaths(manifest, optIns || await readOptIns(cache));
|
||||
const missingFiles = (await Promise.all(files.map(async path => {
|
||||
const response = await cache.match(url(path, manifest.files[path]));
|
||||
return verifiedProbe(response, manifest.files[path].h) ? null : path;
|
||||
@@ -146,12 +160,13 @@
|
||||
}
|
||||
|
||||
const completionKey = manifest => '/__ytp_completion/' + manifest.buildTag;
|
||||
async function complete(manifest, { cache, fetchFn, cycles = 4, now = Date.now,
|
||||
async function complete(manifest, { cache, fetchFn, cycles = 4, now = Date.now, optIns,
|
||||
sleep = ms => new Promise(resolve => setTimeout(resolve, ms)), baseDelay = 1000,
|
||||
maxDelay = 30000, notify = () => {}, force = false } = {}) {
|
||||
const key = completionKey(manifest), saved = await cache.match(key);
|
||||
let job = saved ? await saved.json() : { failures: 0, nextRetryAt: 0 };
|
||||
let snapshot = await completeness(manifest, cache);
|
||||
const selectedOptIns = optIns || await readOptIns(cache);
|
||||
let snapshot = await completeness(manifest, cache, { optIns: selectedOptIns });
|
||||
const publish = async running => {
|
||||
job = { ...job, ...snapshot, running, version: manifest.buildTag };
|
||||
await cache.put(key, Response.json(job));
|
||||
@@ -162,9 +177,9 @@
|
||||
for (let cycle = 0; cycle < cycles; cycle++) {
|
||||
await publish(true);
|
||||
let error;
|
||||
try { await syncAssets(manifest, { cache, fetchFn, all: true }); }
|
||||
try { await syncAssets(manifest, { cache, fetchFn, all: true, optIns: selectedOptIns }); }
|
||||
catch (failure) { error = failure; }
|
||||
snapshot = await completeness(manifest, cache);
|
||||
snapshot = await completeness(manifest, cache, { optIns: selectedOptIns });
|
||||
if (snapshot.offlineReady) { job.failures = 0; job.nextRetryAt = 0; job.error = null; await publish(false); return job; }
|
||||
job.failures++;
|
||||
const delay = Math.min(maxDelay, baseDelay * 2 ** Math.min(job.failures - 1, 10));
|
||||
@@ -198,7 +213,7 @@
|
||||
return next;
|
||||
}
|
||||
|
||||
const api = { CACHE, STATE, url, paths, blocking, plan, retained, fallback, status, completeness, completionKey, complete, syncAssets, state, commit };
|
||||
const api = { CACHE, STATE, OPT_INS, url, paths, offlinePaths, blocking, plan, retained, fallback, status, completeness, completionKey, complete, syncAssets, readOptIns, state, commit };
|
||||
if (typeof module !== 'undefined' && module.exports) module.exports = api;
|
||||
else root.AssetSyncCore = api;
|
||||
})(typeof globalThis !== 'undefined' ? globalThis : this);
|
||||
|
||||
Reference in New Issue
Block a user