feat: add thumbnail metadata and download endpoint
- Extend `BoxFile` with thumbnail path/status fields and internal URL - Populate `ThumbnailURL` when a thumbnail path is present during decoration - Add `/box/:id/thumbnails/:file_id` route and handler to serve JPEG thumbnails - Introduce thumbnail status constants to standardize processing state reportingfeat: add thumbnail metadata and download endpoint - Extend `BoxFile` with thumbnail path/status fields and internal URL - Populate `ThumbnailURL` when a thumbnail path is present during decoration - Add `/box/:id/thumbnails/:file_id` route and handler to serve JPEG thumbnails - Introduce thumbnail status constants to standardize processing state reporting
This commit is contained in:
@@ -19,6 +19,14 @@ let selectedFiles = [];
|
||||
let statusTimer = null;
|
||||
let shareURL = "";
|
||||
|
||||
function revokePreviewURLs() {
|
||||
selectedFiles.forEach((selectedFile) => {
|
||||
if (selectedFile.previewURL) {
|
||||
URL.revokeObjectURL(selectedFile.previewURL);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function formatBytes(bytes) {
|
||||
const units = ["B", "KB", "MB", "GB"];
|
||||
let size = bytes;
|
||||
@@ -167,10 +175,11 @@ function setRowProgress(row, percent) {
|
||||
function createFileRow(selectedFile) {
|
||||
const row = document.createElement("div");
|
||||
row.className = "upload-file-row";
|
||||
row.classList.toggle("has-thumbnail", Boolean(selectedFile.previewURL));
|
||||
|
||||
const icon = document.createElement("img");
|
||||
icon.className = "upload-file-icon";
|
||||
icon.src = iconForFile(selectedFile.file);
|
||||
icon.src = selectedFile.previewURL || iconForFile(selectedFile.file);
|
||||
icon.alt = "";
|
||||
icon.setAttribute("aria-hidden", "true");
|
||||
|
||||
@@ -197,8 +206,10 @@ function createFileRow(selectedFile) {
|
||||
}
|
||||
|
||||
function updateSelectedFiles(files) {
|
||||
revokePreviewURLs();
|
||||
selectedFiles = Array.from(files || []).map((file) => ({
|
||||
file,
|
||||
previewURL: file.type.startsWith("image/") ? URL.createObjectURL(file) : "",
|
||||
loaded: 0,
|
||||
row: null,
|
||||
uploaded: false,
|
||||
@@ -438,7 +449,10 @@ if (uploadForm) {
|
||||
selectedFile.boxID = box.box_id;
|
||||
selectedFile.boxFile = box.files[index];
|
||||
const icon = selectedFile.row.querySelector(".upload-file-icon");
|
||||
if (icon && selectedFile.boxFile.icon_path) {
|
||||
if (icon && selectedFile.boxFile.thumbnail_path) {
|
||||
selectedFile.row.classList.add("has-thumbnail");
|
||||
icon.src = selectedFile.boxFile.thumbnail_path;
|
||||
} else if (icon && selectedFile.boxFile.icon_path && !selectedFile.previewURL) {
|
||||
icon.src = selectedFile.boxFile.icon_path;
|
||||
}
|
||||
});
|
||||
@@ -489,3 +503,5 @@ if (shareButton) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener("beforeunload", revokePreviewURLs);
|
||||
|
||||
@@ -16,12 +16,14 @@ function updateBoxFile(file) {
|
||||
}
|
||||
|
||||
const meta = item.querySelector(".box-file-meta");
|
||||
const icon = item.querySelector(".box-file-icon");
|
||||
const isComplete = file.status === "complete";
|
||||
const isFailed = file.status === "failed";
|
||||
|
||||
item.classList.toggle("is-complete", isComplete);
|
||||
item.classList.toggle("is-failed", isFailed);
|
||||
item.classList.toggle("is-loading", !isComplete && !isFailed);
|
||||
item.classList.toggle("has-thumbnail", Boolean(file.thumbnail_path));
|
||||
item.dataset.status = file.status;
|
||||
item.title = file.title;
|
||||
|
||||
@@ -38,6 +40,10 @@ function updateBoxFile(file) {
|
||||
if (meta) {
|
||||
meta.textContent = `${file.status_label} · ${file.size_label}`;
|
||||
}
|
||||
|
||||
if (icon) {
|
||||
icon.src = file.thumbnail_path || file.icon_path;
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshBoxStatus() {
|
||||
@@ -59,7 +65,11 @@ async function refreshBoxStatus() {
|
||||
boxStatus.textContent = `${completeCount}/${result.files.length} ready`;
|
||||
}
|
||||
|
||||
return result.files.some((file) => file.status === "pending" || file.status === "uploading");
|
||||
return result.files.some((file) => {
|
||||
const isUploading = file.status === "pending" || file.status === "uploading";
|
||||
const isWaitingForThumbnail = file.status === "complete" && !file.thumbnail_status && !file.thumbnail_path;
|
||||
return isUploading || isWaitingForThumbnail || file.thumbnail_status === "processing";
|
||||
});
|
||||
}
|
||||
|
||||
if (boxPanel) {
|
||||
|
||||
Reference in New Issue
Block a user