feat: add YouTube cookies upload via web UI
Adds a Settings panel to upload a cookies.txt file directly from the browser, persisted in a named Docker volume. yt-dlp uses the file when present to bypass YouTube bot detection.
This commit is contained in:
@@ -255,9 +255,23 @@
|
||||
<div class="container">
|
||||
<header>
|
||||
<h1><span>apoena</span> transcript</h1>
|
||||
<span id="device-badge" class="badge badge-loading">Loading...</span>
|
||||
<div style="display:flex;align-items:center;gap:0.6rem">
|
||||
<span id="device-badge" class="badge badge-loading">Loading...</span>
|
||||
<button id="settings-toggle" style="background:transparent;border:1px solid #2a2a38;color:#64748b;padding:0.3rem 0.6rem;font-size:0.8rem;font-weight:400">⚙ Settings</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Settings panel -->
|
||||
<div id="settings-panel" class="card" style="display:none;margin-bottom:1rem">
|
||||
<h2>YouTube Cookies</h2>
|
||||
<p style="font-size:0.82rem;color:#64748b;margin-bottom:0.8rem">Upload a <code>cookies.txt</code> (Netscape format) to bypass YouTube bot detection. Export it from Chrome using the <em>Get cookies.txt LOCALLY</em> extension while logged in to YouTube.</p>
|
||||
<div style="display:flex;gap:0.6rem;align-items:center;flex-wrap:wrap">
|
||||
<input type="file" id="cookies-input" accept=".txt,text/plain" style="font-size:0.85rem;color:#94a3b8" />
|
||||
<button id="cookies-btn" disabled>Upload</button>
|
||||
</div>
|
||||
<div id="cookies-status" style="font-size:0.82rem;color:#64748b;margin-top:0.6rem"></div>
|
||||
</div>
|
||||
|
||||
<!-- Model status -->
|
||||
<div id="model-status" class="model-status">
|
||||
Loading model — first visit downloads ~100 MB, then it's cached locally.
|
||||
@@ -684,6 +698,49 @@ function pad(n, len = 2) { return String(Math.floor(n)).padStart(len, '0'); }
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.register('/sw.js');
|
||||
}
|
||||
|
||||
// ── Settings (cookies upload) ───────────────────────────────────────────────
|
||||
const settingsToggle = document.getElementById('settings-toggle');
|
||||
const settingsPanel = document.getElementById('settings-panel');
|
||||
const cookiesInput = document.getElementById('cookies-input');
|
||||
const cookiesBtn = document.getElementById('cookies-btn');
|
||||
const cookiesStatus = document.getElementById('cookies-status');
|
||||
|
||||
settingsToggle.addEventListener('click', async () => {
|
||||
const open = settingsPanel.style.display !== 'none';
|
||||
settingsPanel.style.display = open ? 'none' : 'block';
|
||||
if (!open) {
|
||||
const res = await fetch('/admin/cookies/status').then(r => r.json()).catch(() => null);
|
||||
if (res) {
|
||||
cookiesStatus.textContent = res.present
|
||||
? `✓ Cookies file present (${(res.size / 1024).toFixed(1)} KB)`
|
||||
: 'No cookies file uploaded yet.';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
cookiesInput.addEventListener('change', () => {
|
||||
cookiesBtn.disabled = !cookiesInput.files.length;
|
||||
});
|
||||
|
||||
cookiesBtn.addEventListener('click', async () => {
|
||||
const file = cookiesInput.files[0];
|
||||
if (!file) return;
|
||||
const form = new FormData();
|
||||
form.append('file', file);
|
||||
cookiesBtn.disabled = true;
|
||||
cookiesStatus.textContent = 'Uploading…';
|
||||
try {
|
||||
const res = await fetch('/admin/cookies', { method: 'POST', body: form });
|
||||
if (!res.ok) throw new Error((await res.json()).detail || res.statusText);
|
||||
cookiesStatus.textContent = '✓ Cookies uploaded successfully.';
|
||||
cookiesInput.value = '';
|
||||
} catch (err) {
|
||||
cookiesStatus.textContent = 'Error: ' + err.message;
|
||||
} finally {
|
||||
cookiesBtn.disabled = false;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user