Send UI downloads into Movies library and harden queue API.

UI grabs write Movies/<Title (Year)>/; chown to 1000:1000; CORS and safer /api/queue handling for the Web UI.
This commit is contained in:
2026-08-07 11:58:11 +02:00
parent 798d7d7251
commit 96dbc46580
5 changed files with 186 additions and 43 deletions

View File

@@ -1,6 +1,25 @@
// Same origin as the page (works for LAN IP / hostname). Avoid relative issues
// when the app is served with a non-root base path later.
const API_BASE = window.location.origin;
let queuePoll = null;
async function apiFetch(path, opts) {
const res = await fetch(`${API_BASE}${path}`, {
...opts,
headers: {
Accept: 'application/json',
...(opts && opts.headers),
},
});
if (!res.ok) {
const text = await res.text().catch(() => '');
throw new Error(text || `HTTP ${res.status}`);
}
const ct = res.headers.get('content-type') || '';
if (ct.includes('application/json')) return res.json();
return res.text();
}
// ── Tabs ────────────────────────────────────────────────────────────────────
function startQueuePoll() {
@@ -51,9 +70,7 @@ async function doSearch() {
if (!q) return;
searchResults.innerHTML = '<div class="spinner">Searching...</div>';
try {
const res = await fetch(`${API_BASE}/api/search?q=${encodeURIComponent(q)}&limit=30`);
if (!res.ok) throw new Error(await res.text());
const data = await res.json();
const data = await apiFetch(`/api/search?q=${encodeURIComponent(q)}&limit=30`);
renderSearchResults(data);
} catch (err) {
searchResults.innerHTML = `<div class="empty">Search failed: ${err.message}</div>`;
@@ -116,12 +133,11 @@ function renderSearchResults(files) {
const addedIdents = {};
async function addDownload(ident, name, size) {
const res = await fetch(`${API_BASE}/api/queue/add`, {
await apiFetch('/api/queue/add', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ident, name, size }),
});
if (!res.ok) throw new Error(await res.text());
addedIdents[ident] = true;
}
@@ -130,9 +146,7 @@ async function addDownload(ident, name, size) {
const queueList = document.getElementById('queueList');
async function fetchQueue() {
const res = await fetch(`${API_BASE}/api/queue`);
if (!res.ok) throw new Error(await res.text());
return res.json();
return apiFetch('/api/queue');
}
function renderQueue() {
@@ -175,7 +189,7 @@ function renderQueue() {
btn.disabled = true;
btn.textContent = '[…]';
try {
await fetch(`${API_BASE}/api/queue/cancel`, {
await apiFetch('/api/queue/cancel', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ hash }),