Show peer availability with stale markers and add Sharing settings
This commit is contained in:
@@ -2434,10 +2434,42 @@ async function reportBroken(video) {
|
||||
}
|
||||
}
|
||||
|
||||
// P2P availability under the title (docs/p2p-architecture.md flow 5).
|
||||
// Cached 60 s per video so the frequent updateNowPlayingActions() calls
|
||||
// don't hammer the server.
|
||||
const availCache = new Map(); // videoId -> { at, payload }
|
||||
async function renderAvailability(id) {
|
||||
const el = $('npAvail');
|
||||
if (!el) return;
|
||||
const off = data.settings.p2pShare === false && data.settings.p2pReceive === false;
|
||||
if (!WEB || !window.P2PCore || off || navigator.onLine === false || String(id).startsWith('edit_')) {
|
||||
el.classList.add('hidden');
|
||||
return;
|
||||
}
|
||||
let hit = availCache.get(id);
|
||||
if (!hit || Date.now() - hit.at > 60_000) {
|
||||
hit = { at: Date.now(), payload: null };
|
||||
availCache.set(id, hit);
|
||||
try {
|
||||
const r = await fetch(`/api/p2p/holders?v=${encodeURIComponent(id)}`);
|
||||
hit.payload = r.ok ? await r.json() : null;
|
||||
} catch { /* offline / disabled — no line */ }
|
||||
if (availCache.size > 200) availCache.delete(availCache.keys().next().value);
|
||||
}
|
||||
if (!current || !current.meta || current.meta.id !== id) return; // moved on meanwhile
|
||||
const f = window.P2PCore.formatAvailability(hit.payload, Date.now());
|
||||
if (!f) { el.classList.add('hidden'); return; }
|
||||
el.textContent = f.text;
|
||||
el.title = f.title;
|
||||
el.classList.toggle('stale', f.stale);
|
||||
el.classList.remove('hidden');
|
||||
}
|
||||
|
||||
// Reflect cache state on the now-playing Save button.
|
||||
function updateNowPlayingActions() {
|
||||
if (!current || !current.meta) return;
|
||||
const id = current.meta.id;
|
||||
renderAvailability(id);
|
||||
// Custom (edited) videos are already a saved-offline cut of a source video;
|
||||
// re-saving or re-editing them makes no sense, so hide those actions.
|
||||
const isCustom = !!current.meta.custom;
|
||||
@@ -7453,6 +7485,28 @@ async function renderSettings() {
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="set-group">
|
||||
<div class="set-group-title">Sharing (peer-to-peer)</div>
|
||||
<label class="set-row">
|
||||
<span>
|
||||
Share my saved videos
|
||||
<small>Other devices can download videos you saved when YouTube and the server can't provide them. Files are verified by their hash. On by default.</small>
|
||||
</span>
|
||||
<input id="setP2pShare" type="checkbox" ${data.settings.p2pShare !== false ? 'checked' : ''} />
|
||||
</label>
|
||||
<label class="set-row">
|
||||
<span>
|
||||
Get videos from other devices
|
||||
<small>When a video is gone from YouTube and the server, download a verified copy from a device that has it. On by default.</small>
|
||||
</span>
|
||||
<input id="setP2pReceive" type="checkbox" ${data.settings.p2pReceive !== false ? 'checked' : ''} />
|
||||
</label>
|
||||
<div class="set-row">
|
||||
<span>This device<small id="p2pDeviceInfo">…</small></span>
|
||||
<span id="p2pHoldingCount" class="set-stat">…</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="set-group">
|
||||
<div class="set-group-title">Offline cache</div>
|
||||
<label class="set-row">
|
||||
@@ -7621,6 +7675,27 @@ async function renderSettings() {
|
||||
persist();
|
||||
if (e.target.checked) data.playlists.forEach(preloadPlaylist);
|
||||
});
|
||||
$('setP2pShare').addEventListener('change', (e) => {
|
||||
data.settings.p2pShare = e.target.checked;
|
||||
persist();
|
||||
if (window.P2PClient) window.P2PClient.sync(); // share off → withdraws every holding
|
||||
});
|
||||
$('setP2pReceive').addEventListener('change', (e) => {
|
||||
data.settings.p2pReceive = e.target.checked;
|
||||
persist();
|
||||
if (window.P2PClient) window.P2PClient.sync();
|
||||
});
|
||||
(async () => {
|
||||
const info = $('p2pDeviceInfo');
|
||||
const cnt = $('p2pHoldingCount');
|
||||
if (!info || !cnt) return;
|
||||
const P = window.P2PClient;
|
||||
const d = P && P.device();
|
||||
info.textContent = !d ? 'Not registered yet'
|
||||
: P.isConnected() ? `Online · peer ${P.peer()}` : 'Registered · not connected';
|
||||
const files = window.DeviceDB ? await window.DeviceDB.listFiles() : [];
|
||||
cnt.textContent = `${files.filter((f) => f.state === 'verified').length} verified / ${files.length} saved`;
|
||||
})();
|
||||
|
||||
// ---- Cache management ----
|
||||
$('clearCacheBtn').addEventListener('click', () => {
|
||||
|
||||
Reference in New Issue
Block a user