Files

97 lines
4.4 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="dark">
<meta name="theme-color" content="#111827">
<title>Immich Share Sync</title>
<link rel="stylesheet" href="/styles.css">
</head>
<body>
<a class="source-ribbon" href="https://code.brunner.ninja/feedc0de/immich-sync" target="_blank" rel="noopener noreferrer" aria-label="View and fork Immich Share Sync at code.brunner.ninja">
<svg viewBox="0 0 24 24" aria-hidden="true">
<path d="m8.7 16.6-4.6-4.6 4.6-4.6 1.4 1.4L6.9 12l3.2 3.2-1.4 1.4Zm6.6 0-1.4-1.4 3.2-3.2-3.2-3.2 1.4-1.4 4.6 4.6-4.6 4.6ZM10.4 19l2.9-14 2 .4-2.9 14-2-.4Z"/>
</svg>
<span>Fork me at code.brunner.ninja</span>
</a>
<div class="ambient ambient-one"></div>
<div class="ambient ambient-two"></div>
<main>
<header class="hero">
<div class="brand-mark" aria-hidden="true"><span></span><span></span><span></span><span></span><span></span><span></span></div>
<div>
<p class="eyebrow">Independent Immich servers, one complete album</p>
<h1>Immich Share Sync</h1>
<p class="lede">Compare two public shares by checksum and stream only the missing originals. Nothing is deleted and nothing is stored here.</p>
</div>
<div id="connection" class="connection idle"><i></i><span>Idle</span></div>
</header>
<section class="workspace" aria-label="Share links">
<article id="leftCard" class="share-card left">
<div class="card-heading">
<span class="side-label">A</span>
<div><h2>Left share</h2><p id="leftStatus">Waiting for a link</p></div>
<span id="leftState" class="state idle">Idle</span>
</div>
<label>Immich share URL
<input id="leftUrl" type="url" autocomplete="url" spellcheck="false" placeholder="https://photos.example/share/…" required>
</label>
<label>Password <span>if protected</span>
<input id="leftPassword" type="password" autocomplete="off" placeholder="Optional">
</label>
<div id="leftDetails" class="details hidden"></div>
</article>
<div class="exchange" aria-hidden="true">
<span></span><span></span>
</div>
<article id="rightCard" class="share-card right">
<div class="card-heading">
<span class="side-label">B</span>
<div><h2>Right share</h2><p id="rightStatus">Waiting for a link</p></div>
<span id="rightState" class="state idle">Idle</span>
</div>
<label>Immich share URL
<input id="rightUrl" type="url" autocomplete="url" spellcheck="false" placeholder="https://photos.example/share/…" required>
</label>
<label>Password <span>if protected</span>
<input id="rightPassword" type="password" autocomplete="off" placeholder="Optional">
</label>
<div id="rightDetails" class="details hidden"></div>
</article>
</section>
<section class="controls">
<button id="inspect" class="primary" type="button">Inspect both shares</button>
<p id="inspectionMessage">Connect both sides to discover available synchronization directions.</p>
<div id="syncOptions" class="sync-options hidden">
<button type="button" data-direction="left-to-right"><strong>A → B</strong><span></span></button>
<button type="button" data-direction="bidirectional"><strong>A ↔ B</strong><span></span></button>
<button type="button" data-direction="right-to-left"><strong>B → A</strong><span></span></button>
</div>
</section>
<section id="activity" class="activity hidden" aria-live="polite">
<div class="activity-heading">
<div><p class="eyebrow">Live WebSocket status</p><h2 id="activityTitle">Synchronization</h2></div>
<button id="abort" class="danger" type="button">Abort &amp; disconnect</button>
</div>
<div class="progress-track"><div id="progressBar"></div></div>
<div class="progress-copy"><span id="progressText">Preparing…</span><span id="progressCount">0 / 0</span></div>
<ol id="events" class="events"></ol>
</section>
<footer>
<span>Ephemeral by design</span>
<span>Checksum comparison</span>
<span>Originals streamed in memory</span>
<span>No deletion</span>
</footer>
</main>
<script src="/app.js" defer></script>
</body>
</html>