--- a/frontend/p2p-client.js +++ b/frontend/p2p-client.js @@ -160,7 +160,7 @@ // Send one saved video to the server so it can hash + validate it and add its // cid to the catalog. Uploads the whole file: only on the user's request // ("Verify & share") or when the server asks for a copy (plan 018). - async function contribute(videoId, { cid = null, title = '', channel = '' } = {}) { + async function contribute(videoId, { cid = null, title = '', channel = '', restore = false } = {}) { const dev = await ensureDevice(); const rec = await window.DeviceDB.getFile(videoId); const file = window.OPFS.getFileObject ? await window.OPFS.getFileObject(videoId) : null; @@ -168,7 +168,7 @@ const h = { 'X-Device': dev.deviceId + '.' + dev.secret }; const t = await (await fetch('/api/p2p/intake', { method: 'POST', headers: { ...h, 'Content-Type': 'application/json' }, - body: JSON.stringify({ videoId, cid: cid || (rec && rec.cid) || undefined, size: file.size, title, channel }), + body: JSON.stringify({ videoId, cid: cid || (rec && rec.cid) || undefined, size: file.size, title, channel, restore }), })).json().catch(() => ({ ok: false, error: 'server unreachable' })); if (!t.ok || t.known) { if (t.known) changed(); return t; } const r = await (await fetch(t.url, { method: 'PUT', headers: h, body: file })) @@ -280,6 +280,19 @@ timer = setTimeout(sync, 5000); } + // Plan 018: the server lost its copy and the source is gone — it asks one + // holder to send the file back. Only while sharing is on, one at a time, + // and only for the exact cid this device holds. + let restoring = false; + onMessage('upload-request', async (m) => { + if (restoring || hooks.getSettings().p2pShare === false) return; + const rec = window.DeviceDB ? await window.DeviceDB.getFile(m.videoId) : null; + if (!rec || rec.cid !== m.cid) return; + restoring = true; + try { await contribute(m.videoId, { cid: m.cid, restore: true }); } catch { /* next request retries */ } + finally { restoring = false; } + }); + function start(h) { hooks = { ...hooks, ...(h || {}) }; const idle = window.requestIdleCallback || ((fn) => setTimeout(fn, 1));