Put the media seed behind MEDIA_SEED_ENABLED so a partner filled by other means never starts a multi-week transfer

Tier 2 beside the per-orchestrator gates. Unset reads as on — the toggle postdates the seed.
This commit is contained in:
Gmer4Lfe
2026-08-17 07:46:51 -04:00
parent ad623353bc
commit 1a47f864f9
7 changed files with 139 additions and 37 deletions
+20 -7
View File
@@ -245,10 +245,22 @@ function vv_pt_remote_system(string $ip, string $sshKey): array {
function vv_pt_media_seed(): array {
$stat = '/var/log/varaverk/Rsync/media_seed.json';
$log = '/var/log/varaverk/Rsync/media_seed.log';
if (!is_file($stat)) return [];
// MEDIA_SEED_ENABLED is reported alongside the record rather than instead of it, because
// the two answer different questions: the toggle says whether a seed may start, the record
// says what the last one did. media_seed.sh exits 0 when the toggle is off — correct, it is
// a decision and not a failure — so a record reading "ok" beside a disabled toggle would
// otherwise render as "Seed complete" over a partner that was never seeded.
//
// Unset reads as enabled, matching media_seed.sh: the toggle postdates the script.
$raw = vv_read_conf_raw('master.conf');
$toggle = vv_arr_scalar($raw, 'MEDIA_SEED_ENABLED');
$enabled = ($toggle === '' || strtolower($toggle) === 'true');
if (!is_file($stat)) return ['enabled' => $enabled];
$j = json_decode((string)file_get_contents($stat), true);
if (!is_array($j)) return [];
if (!is_array($j)) return ['enabled' => $enabled];
$status = (string)($j['status'] ?? '');
$pid = (int)($j['pid'] ?? 0);
@@ -280,11 +292,12 @@ function vv_pt_media_seed(): array {
}
return array_filter([
'status' => $status,
'start' => isset($j['start']) ? (int)$j['start'] : null,
'end' => isset($j['end']) ? (int)$j['end'] : null,
'share' => $share,
'line' => mb_substr($line, 0, 160),
'enabled' => $enabled,
'status' => $status,
'start' => isset($j['start']) ? (int)$j['start'] : null,
'end' => isset($j['end']) ? (int)$j['end'] : null,
'share' => $share,
'line' => mb_substr($line, 0, 160),
], fn($v) => $v !== null && $v !== '');
}
+55 -20
View File
@@ -381,6 +381,29 @@ function vvPtHostChanged(el) {
// Stop the background media seed. Safe to press: rsync.sh runs --inplace --partial, so this
// costs the file in flight and a later start resumes the share rather than restarting it.
// Start the background seed. Nothing here decides whether it may run — MEDIA_SEED_ENABLED is
// checked by the script, and the button is not rendered when the toggle is off.
async function vvPtStartSeed(btn) {
if (!await vvConfirm('Start the media seed?\n\nEvery DAILY_SYNC_SHARES entry is pushed to the partner. A first seed of a full library runs for days.')) return;
btn.disabled = true; btn.textContent = '⟳';
try {
const r = await fetch('/plugins/varaverk/api/media_seed.php', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
csrf_token: typeof csrf_token !== 'undefined' ? csrf_token : '',
action: 'start'
})
});
const d = await r.json();
if (!d.ok) { vvAlert('Seed did not start: ' + (d.error ?? 'Unknown error')); btn.disabled = false; btn.textContent = '▶ Seed'; return; }
if (typeof vvPtLoad === 'function') vvPtLoad();
} catch (e) {
btn.disabled = false; btn.textContent = '▶ Seed';
vvAlert('Error: ' + e);
}
}
async function vvPtStopSeed(btn) {
if (!await vvConfirm('Stop the media seed?\n\nThe transfer resumes from where it stopped when restarted.')) return;
btn.disabled = true; btn.textContent = '⟳';
@@ -1151,27 +1174,39 @@ function _renderActions(nodes, cfg) {
// Media seed strip. The seed is dispatched detached by onboard Step 13 and runs for
// days, so without this the page would show a finished onboard and no sign that
// terabytes are still moving underneath it.
// The toggle is read first and the record second. media_seed.sh exits 0 when
// MEDIA_SEED_ENABLED is false, so the record can read "ok" for a seed that never
// moved a byte — showing that as "Seed complete" would be exactly the kind of
// outcome-from-a-toggle claim this codebase keeps having to unpick.
const ms = remote.media_seed || {};
if (ms.status) {
const seedRunning = ms.status === 'running';
const seedColor = seedRunning ? '#ff9800'
: ms.status === 'ok' ? '#4caf50'
: ms.status === 'stopped' ? '#666' : '#f44336';
const seedLabel = seedRunning ? 'Seeding'
: ms.status === 'ok' ? 'Seed complete'
: ms.status === 'stopped' ? 'Seed stopped' : 'Seed failed';
html += `<div style="margin-top:9px;padding-top:8px;border-top:1px solid #181818;">
<div style="display:flex;align-items:center;gap:7px;flex-wrap:wrap;">
<span style="font-size:9px;font-weight:600;color:${seedColor};">${seedLabel}</span>
${ms.share ? `<span style="font-size:9px;color:#444;font-family:monospace;">${vvEscHtml(ms.share)}</span>` : ''}
${seedRunning ? `<button class="vv-pt-action-btn warn" onclick="vvPtStopSeed(this)"
style="font-size:9px;opacity:.55;margin-left:auto;">■ Stop</button>` : ''}
</div>
${ms.line ? `<div style="margin-top:3px;font-size:9px;color:#2e2e2e;font-family:monospace;
white-space:nowrap;overflow:hidden;text-overflow:ellipsis;"
>${vvEscHtml(ms.line)}</div>` : ''}
</div>`;
}
const seedRunning = ms.status === 'running';
let seedColor, seedLabel;
if (ms.enabled === false && !seedRunning) {
seedColor = '#555'; seedLabel = 'Seed off';
} else if (seedRunning) { seedColor = '#ff9800'; seedLabel = 'Seeding'; }
else if (ms.status === 'ok') { seedColor = '#4caf50'; seedLabel = 'Seed complete'; }
else if (ms.status === 'stopped'){ seedColor = '#666'; seedLabel = 'Seed stopped'; }
else if (ms.status) { seedColor = '#f44336'; seedLabel = 'Seed failed'; }
else { seedColor = '#555'; seedLabel = 'Not seeded'; }
const seedOff = ms.enabled === false;
const showLine = seedRunning || (ms.line && !seedOff);
html += `<div style="margin-top:9px;padding-top:8px;border-top:1px solid #181818;">
<div style="display:flex;align-items:center;gap:7px;flex-wrap:wrap;">
<span style="font-size:9px;font-weight:600;color:${seedColor};">${seedLabel}</span>
${seedOff ? `<span style="font-size:9px;color:#333;">MEDIA_SEED_ENABLED=false</span>` : ''}
${!seedOff && ms.share ? `<span style="font-size:9px;color:#444;font-family:monospace;">${vvEscHtml(ms.share)}</span>` : ''}
${seedRunning
? `<button class="vv-pt-action-btn warn" onclick="vvPtStopSeed(this)"
style="font-size:9px;opacity:.55;margin-left:auto;">■ Stop</button>`
: (seedOff ? '' : `<button class="vv-pt-action-btn info" onclick="vvPtStartSeed(this)"
style="font-size:9px;opacity:.55;margin-left:auto;"
title="Push every DAILY_SYNC_SHARES entry to ${remote.id}">▶ Seed</button>`)}
</div>
${showLine ? `<div style="margin-top:3px;font-size:9px;color:#2e2e2e;font-family:monospace;
white-space:nowrap;overflow:hidden;text-overflow:ellipsis;"
>${vvEscHtml(ms.line || '')}</div>` : ''}
</div>`;
}
// ── Delete Keys panel ──────────────────────────────────────────────────
+1
View File
@@ -1788,6 +1788,7 @@ Saved into `master.conf`, which does not need to be opened by hand.
| `WEEKLY_RSYNC_ENABLED` | a switch | in this section | Tier 2 — weekly_sync_maintenance.sh rsync section |
| `MONTHLY_RSYNC_ENABLED` | a switch | in this section | Tier 2 — monthly_maintenance.sh rsync section |
| `FALLBACK_RSYNC_ENABLED` | a switch | Fallback tab | Tier 2 — fallback.sh writeback jobs on handback |
| `MEDIA_SEED_ENABLED` | a switch | Partnership tab | Tier 2 — Rsync/media_seed.sh, the onboard first-fill of a new partner |
## Rsync Merge Auto-Promote