download taken zichtbaar gemaakt

This commit is contained in:
kodi
2026-03-14 15:57:45 +01:00
parent e85e51d64a
commit 5265d6458c
5 changed files with 166 additions and 9 deletions
+150 -9
View File
@@ -101,6 +101,8 @@ let folderUploadPickerInput = null;
let settingsState = {
activeTab: "general",
logsLoaded: false,
tasksLoaded: false,
logsPollTimer: null,
showThumbnails: false,
preferredStartupPathLeft: null,
preferredStartupPathRight: null,
@@ -903,6 +905,7 @@ function settingsElements() {
downloadScanTimeout: document.getElementById("settings-download-scan-timeout"),
downloadSymlinkPolicy: document.getElementById("settings-download-symlink-policy"),
logsPanel: document.getElementById("settings-logs-panel"),
tasksList: document.getElementById("settings-tasks-list"),
logsList: document.getElementById("settings-logs-list"),
logsError: document.getElementById("settings-logs-error"),
};
@@ -3726,6 +3729,68 @@ function formatHistoryLine(item) {
};
}
function formatTaskStatusLabel(task) {
if (task.operation === "download") {
switch (task.status) {
case "requested":
return "Requested";
case "preparing":
return "Preparing";
case "ready":
return "Ready for download";
case "failed":
return "Failed";
case "cancelled":
return "Cancelled";
default:
return task.status;
}
}
switch (task.status) {
case "queued":
return "Queued";
case "running":
return "Running";
case "completed":
return "Completed";
case "failed":
return "Failed";
default:
return task.status;
}
}
function inferDownloadTaskContext(task) {
if (task.operation !== "download") {
return null;
}
if (typeof task.destination === "string" && /^kodidownload-\d{8}-\d{6}\.zip$/.test(task.destination)) {
return "Multi-item ZIP";
}
return "Directory ZIP";
}
function formatTaskLine(task) {
const when = formatModified(task.finished_at || task.created_at || "");
const details = [];
const downloadContext = inferDownloadTaskContext(task);
if (downloadContext) {
details.push(downloadContext);
}
if (typeof task.done_items === "number" && typeof task.total_items === "number") {
details.push(`${task.done_items}/${task.total_items} items`);
}
if (task.current_item) {
details.push(`Current: ${task.current_item}`);
}
return {
title: `${task.operation} · ${formatTaskStatusLabel(task)}`,
path: task.destination ? `${task.destination} · ${task.source}` : task.source || "-",
meta: [when, ...details].filter(Boolean).join(" · "),
error: task.status === "failed" ? (task.error_message || task.error_code || "") : "",
};
}
function renderHistoryItems(items) {
const elements = settingsElements();
elements.logsList.innerHTML = "";
@@ -3760,20 +3825,84 @@ function renderHistoryItems(items) {
}
}
function renderTaskItems(items) {
const elements = settingsElements();
elements.tasksList.innerHTML = "";
if (!Array.isArray(items) || items.length === 0) {
const empty = document.createElement("div");
empty.className = "popup-meta";
empty.textContent = "No tasks yet.";
elements.tasksList.append(empty);
return;
}
for (const task of items) {
const line = formatTaskLine(task);
const row = document.createElement("div");
row.className = `settings-log-item status-${task.status}`;
const title = document.createElement("div");
title.className = "settings-log-title";
title.textContent = line.title;
const path = document.createElement("div");
path.className = "settings-log-path";
path.textContent = line.path;
const meta = document.createElement("div");
meta.className = "settings-log-meta";
meta.textContent = line.meta;
row.append(title, path, meta);
if (line.error) {
const error = document.createElement("div");
error.className = "settings-log-error";
error.textContent = line.error;
row.append(error);
}
elements.tasksList.append(row);
}
}
async function loadHistoryForSettings() {
const data = await apiRequest("GET", "/api/history");
renderHistoryItems(data.items || []);
settingsState.logsLoaded = true;
}
async function loadTasksForSettings() {
const data = await apiRequest("GET", "/api/tasks");
renderTaskItems(data.items || []);
settingsState.tasksLoaded = true;
}
async function loadLogsAndTasksForSettings() {
const elements = settingsElements();
elements.logsError.textContent = "";
elements.tasksList.innerHTML = '<div class="popup-meta">Loading...</div>';
elements.logsList.innerHTML = '<div class="popup-meta">Loading...</div>';
try {
const data = await apiRequest("GET", "/api/history");
renderHistoryItems(data.items || []);
settingsState.logsLoaded = true;
await Promise.all([loadTasksForSettings(), loadHistoryForSettings()]);
} catch (err) {
elements.tasksList.innerHTML = "";
elements.logsList.innerHTML = "";
elements.logsError.textContent = err.message;
}
}
function stopSettingsLogsPolling() {
if (settingsState.logsPollTimer) {
window.clearTimeout(settingsState.logsPollTimer);
settingsState.logsPollTimer = null;
}
}
function scheduleSettingsLogsPolling() {
stopSettingsLogsPolling();
if (settingsState.activeTab !== "logs" || settingsElements().overlay.classList.contains("hidden")) {
return;
}
settingsState.logsPollTimer = window.setTimeout(async () => {
await loadLogsAndTasksForSettings();
scheduleSettingsLogsPolling();
}, 1500);
}
async function handleShowThumbnailsChange(event) {
const input = event.target;
try {
@@ -3814,6 +3943,7 @@ async function handleInterfaceSave() {
}
function closeSettings() {
stopSettingsLogsPolling();
settingsElements().overlay.classList.add("hidden");
}
@@ -3823,7 +3953,10 @@ async function openSettings(tab = "general") {
elements.generalError.textContent = "";
setSettingsTab(tab);
if (settingsState.activeTab === "logs") {
await loadHistoryForSettings();
await loadLogsAndTasksForSettings();
scheduleSettingsLogsPolling();
} else {
stopSettingsLogsPolling();
}
(settingsState.activeTab === "logs"
? elements.logsTab
@@ -4520,12 +4653,20 @@ function setupEvents() {
const settings = settingsElements();
settings.closeButton.onclick = closeSettings;
settings.generalTab.onclick = () => setSettingsTab("general");
settings.interfaceTab.onclick = () => setSettingsTab("interface");
settings.downloadsTab.onclick = () => setSettingsTab("downloads");
settings.generalTab.onclick = () => {
stopSettingsLogsPolling();
setSettingsTab("general");
};
settings.interfaceTab.onclick = () => {
stopSettingsLogsPolling();
setSettingsTab("interface");
};
settings.downloadsTab.onclick = () => {
stopSettingsLogsPolling();
setSettingsTab("downloads");
};
settings.logsTab.onclick = async () => {
setSettingsTab("logs");
await loadHistoryForSettings();
await openSettings("logs");
};
settings.showThumbnailsInput.onchange = handleShowThumbnailsChange;
settings.generalSaveButton.onclick = handlePreferredStartupPathSave;