Files
Media-Server-Setup/clips/index.html

98 lines
6.5 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Vanguard Clips</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
:root{--bg:#1e1e1e;--fg:#e6e6e6;--mut:#9a9a9a;--acc:#3d8bfd;--ok:#2ecc71;--bad:#e74c3c;--box:#2a2a2a}
html,body{margin:0;background:var(--bg);color:var(--fg);font:15px/1.5 system-ui,sans-serif}
main{max-width:640px;margin:0 auto;padding:24px 16px}
h1{font-size:22px;margin:0 0 4px}.sub{color:var(--mut);margin-bottom:20px}
.card{background:var(--box);border:1px solid #3a3a3a;border-radius:12px;padding:18px;margin-bottom:14px}
.card h2{font-size:15px;margin:0 0 10px;color:var(--mut);text-transform:uppercase;letter-spacing:.06em}
ol{padding-left:20px;margin:8px 0}li{margin:6px 0}
code,.id{font-family:ui-monospace,monospace;background:#141414;padding:2px 6px;border-radius:5px;word-break:break-all}
.id{display:block;padding:10px;font-size:14px;margin:6px 0}
input{width:100%;box-sizing:border-box;padding:10px;border:1px solid #444;border-radius:8px;background:#141414;color:var(--fg);font:14px ui-monospace,monospace}
button{padding:10px 14px;border:0;border-radius:8px;background:var(--acc);color:#fff;font-weight:600;cursor:pointer;margin-top:10px}
button.small{background:#3a3a3a;font-weight:500;padding:6px 10px;margin:0 0 0 8px;font-size:13px}
.status{margin-top:10px;color:var(--mut);white-space:pre-wrap}.ok{color:var(--ok)}.bad{color:var(--bad)}
.pill{display:inline-block;padding:2px 8px;border-radius:999px;font-size:12px;background:#333;margin-right:6px}
.pill.on{background:#1e4620;color:#9f9}.pill.off{background:#4a1e1e;color:#f99}
</style>
</head>
<body>
<main>
<h1>Send your clips to Vanguard</h1>
<div class="sub">Logged in as <b id="who">…</b>. Your clips land in <code id="path">clips/…</code> and show up in Jellyfin under your name.</div>
<div class="card">
<h2>Status</h2>
<div id="pills"><span class="pill">loading…</span></div>
<div class="status" id="status"></div>
</div>
<div class="card">
<h2>1. Install Syncthing on your gaming PC</h2>
<ol>
<li>Windows: <a href="https://syncthing.net/downloads/" target="_blank" rel="noopener">Syncthing</a> or <a href="https://github.com/Martchus/syncthingtray/releases" target="_blank" rel="noopener">Syncthing Tray</a>. Mac/Linux: Syncthing.</li>
<li>Open its page (usually <code>http://localhost:8384</code>) → <b>Actions → Show ID</b>. Copy the Device ID.</li>
</ol>
</div>
<div class="card">
<h2>2. Paste your Device ID here</h2>
<input id="devid" placeholder="XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX" autocomplete="off" spellcheck="false">
<button id="go">Connect my device</button>
<div class="status" id="goStatus"></div>
<div class="sub" style="margin:10px 0 0">This creates your folder on the server, adds your device, and shares a receive-only folder named <code id="fid">clips-…</code> with you.</div>
</div>
<div class="card">
<h2>3. Accept on your PC</h2>
<ol>
<li>Your Syncthing shows <b>“New Device”</b> from <b>Vanguard</b> → Add Device. Our ID, for checking:
<span class="id" id="srvid">…</span><button class="small" id="copyid">copy</button></li>
<li>Then <b>“New Folder”</b> <code id="fid2">clips-…</code> → <b>Add</b>. Folder Path = the folder your clips save to
(ShadowPlay / Medal / Steam). Folder Type: <b>Send Only</b>. Save.</li>
<li>Done. Only video files are pulled; your folder layout is mirrored. The server converts odd formats and
keeps one copy per clip.</li>
</ol>
</div>
</main>
<script>
const $ = id => document.getElementById(id);
async function refresh(){
try {
const r = await fetch('/me/clips', {credentials:'same-origin'}); const d = await r.json();
if (!r.ok) throw new Error(d.error || r.status);
$('who').textContent = d.user; $('path').textContent = 'clips/' + d.user; $('fid').textContent = $('fid2').textContent = d.folder_id;
$('srvid').textContent = d.server_device_id || '(syncthing unreachable)';
const pills = [];
pills.push(`<span class="pill ${d.folder_exists?'on':'off'}">server folder ${d.folder_exists?'ready':'missing'}</span>`);
pills.push(`<span class="pill ${d.folder_configured?'on':'off'}">share ${d.folder_configured?'configured':'not yet'}</span>`);
if (d.device_id) pills.push(`<span class="pill ${d.device_connected?'on':'off'}">your device ${d.device_connected?'connected':'not connected'}</span>`);
if (d.files_here != null) pills.push(`<span class="pill on">${d.files_here} file(s) here</span>`);
if (d.quota_gb) pills.push(`<span class="pill ${d.paused_for_quota?'off':(d.used_gb > d.quota_gb*0.9?'':'on')}">${d.used_gb} / ${d.quota_gb} GB${d.paused_for_quota?' — PAUSED (over quota)':''}</span>`);
if (d.remote_completion != null && d.remote_completion < 100) pills.push(`<span class="pill">syncing ${Math.round(d.remote_completion)}%</span>`);
$('pills').innerHTML = pills.join('');
$('status').textContent = d.error ? d.error : (d.device_id ? `paired with ${d.device_name || 'your device'} (${d.device_id.slice(0,7)}…)` : 'not paired yet — do step 2');
if (d.paused_for_quota) $('status').textContent += `\nYour folder is over its ${d.quota_gb} GB quota, so syncing is paused. Delete older clips from your clip folder (they are removed here too) or ask an admin to raise the quota; syncing resumes automatically under 90 %.`;
if (d.device_id) $('devid').value = d.device_id;
} catch (e) { $('pills').innerHTML = `<span class="pill off">error</span>`; $('status').textContent = e.message; $('status').className = 'status bad'; }
}
$('go').onclick = async () => {
$('goStatus').textContent = 'working…'; $('goStatus').className = 'status';
try {
const r = await fetch('/me/clips', {method:'POST', credentials:'same-origin', headers:{'Content-Type':'application/json'}, body: JSON.stringify({device_id: $('devid').value})});
const d = await r.json(); if (!r.ok) throw new Error(d.error || r.status);
$('goStatus').textContent = 'done — now accept the device and the folder in your Syncthing (step 3)'; $('goStatus').className = 'status ok'; refresh();
} catch (e) { $('goStatus').textContent = e.message; $('goStatus').className = 'status bad'; }
};
$('copyid').onclick = async () => { try { await navigator.clipboard.writeText($('srvid').textContent); $('copyid').textContent = 'copied'; setTimeout(()=>$('copyid').textContent='copy', 1500); } catch {} };
refresh(); setInterval(refresh, 10000);
</script>
</body>
</html>