feat: download - fase 02

This commit is contained in:
kodi
2026-03-14 12:40:41 +01:00
parent 610a648fd1
commit dab87878cc
9 changed files with 215 additions and 45 deletions
+20 -11
View File
@@ -384,15 +384,15 @@ function openContextMenu(pane, entry, event) {
const isMulti = items.length > 1;
const openableSingle = items.length === 1 && isOpenableSelection(items[0]);
const editableSingle = items.length === 1 && isEditableSelection(items[0]);
const downloadableSingle = items.length === 1 && items[0].kind === "file";
const downloadableSelection = items.length > 0;
elements.scope.textContent = isMulti ? "Multi-selection" : "Single item";
elements.target.textContent = isMulti ? `${items.length} selected items` : entry.name;
elements.openButton.classList.toggle("hidden", isMulti);
elements.openButton.disabled = !openableSingle;
elements.editButton.classList.toggle("hidden", isMulti || items.length !== 1 || items[0].kind !== "file");
elements.editButton.disabled = !editableSingle;
elements.downloadButton.classList.toggle("hidden", !downloadableSingle);
elements.downloadButton.disabled = !downloadableSingle;
elements.downloadButton.classList.remove("hidden");
elements.downloadButton.disabled = !downloadableSelection;
elements.renameButton.classList.toggle("hidden", isMulti);
elements.copyButton.classList.remove("hidden");
elements.copyButton.disabled = items.length === 0;
@@ -496,21 +496,21 @@ function startContextMenuEdit() {
async function startDownloadSelected() {
const selectedItems = activePaneState().selectedItems;
if (selectedItems.length !== 1 || selectedItems[0].kind !== "file") {
if (selectedItems.length === 0) {
return;
}
const selected = selectedItems[0];
try {
const blob = await downloadFileRequest(selected.path);
const selected = selectedItems[0];
const { blob, fileName } = await downloadFileRequest(selectedItems.map((item) => item.path));
const url = URL.createObjectURL(blob);
const anchor = document.createElement("a");
anchor.href = url;
anchor.download = selected.name;
anchor.download = fileName || selected.name;
document.body.append(anchor);
anchor.click();
anchor.remove();
URL.revokeObjectURL(url);
setStatus(`Download started: ${selected.name}`);
setStatus(`Download started: ${anchor.download}`);
} catch (err) {
setActionError("Download", err);
}
@@ -782,13 +782,22 @@ function createApiError(response, data) {
return err;
}
async function downloadFileRequest(path) {
const response = await fetch(`/api/files/download?${new URLSearchParams({ path }).toString()}`);
async function downloadFileRequest(paths) {
const params = new URLSearchParams();
for (const path of paths) {
params.append("path", path);
}
const response = await fetch(`/api/files/download?${params.toString()}`);
if (!response.ok) {
const data = await response.json().catch(() => ({}));
throw createApiError(response, data);
}
return response.blob();
const disposition = response.headers.get("content-disposition") || "";
const match = disposition.match(/filename=\"([^\"]+)\"/);
return {
blob: await response.blob(),
fileName: match ? match[1] : null,
};
}
async function uploadFileRequest(targetPath, file, overwrite = false) {