File Manager: external link, notes and photos on library files (#3128)

This commit is contained in:
Thomansky
2026-09-26 08:56:48 +02:00
committed by GitHub
parent 222f28c7f8
commit 12dddada0a
49 changed files with 4817 additions and 57 deletions
+57
View File
@@ -7479,8 +7479,54 @@ export const api = {
window.URL.revokeObjectURL(url);
},
getLibraryFileThumbnailUrl: (id: number) => withMediaToken(`${API_BASE}/library/files/${id}/thumbnail`),
// Client-rendered preview thumbnail upload (#2976). STEP/PDF/spreadsheet
// previews render in the browser; the first render is posted back so the
// grid gets a thumbnail without a server-side renderer for those formats.
uploadLibraryPreviewThumbnail: async (fileId: number, thumbnail: Blob): Promise<{ updated: boolean }> => {
const formData = new FormData();
formData.append('thumbnail', thumbnail, 'preview.png');
const headers: Record<string, string> = {};
if (authToken) {
headers['Authorization'] = `Bearer ${authToken}`;
}
const response = await fetch(`${API_BASE}/library/files/${fileId}/preview-thumbnail`, {
method: 'POST',
headers,
body: formData,
});
if (!response.ok) {
const error = await response.json().catch(() => ({}));
throw new Error(error.detail || `HTTP ${response.status}`);
}
return response.json();
},
getLibraryFilePlateThumbnail: (id: number, plateIndex: number) =>
withMediaToken(`${API_BASE}/library/files/${id}/plate-thumbnail/${plateIndex}`),
// Photos of the printed result (#3077) — same shape as the archive photo API.
getLibraryFilePhotoUrl: (fileId: number, filename: string) =>
withMediaToken(`${API_BASE}/library/files/${fileId}/photos/${encodeURIComponent(filename)}`),
uploadLibraryFilePhoto: async (fileId: number, file: File): Promise<{ status: string; filename: string; photos: string[] }> => {
const formData = new FormData();
formData.append('file', file);
const headers: Record<string, string> = {};
if (authToken) {
headers['Authorization'] = `Bearer ${authToken}`;
}
const response = await fetch(`${API_BASE}/library/files/${fileId}/photos`, {
method: 'POST',
headers,
body: formData,
});
if (!response.ok) {
const error = await response.json().catch(() => ({}));
throw new Error(error.detail || `HTTP ${response.status}`);
}
return response.json();
},
deleteLibraryFilePhoto: (fileId: number, filename: string) =>
request<{ status: string; photos: string[] }>(`/library/files/${fileId}/photos/${encodeURIComponent(filename)}`, {
method: 'DELETE',
}),
getLibraryFileGcodeUrl: (id: number) => `${API_BASE}/library/files/${id}/gcode`,
moveLibraryFiles: (fileIds: number[], folderId: number | null) =>
request<{ status: string; moved: number }>('/library/files/move', {
@@ -8046,6 +8092,11 @@ export interface LibraryFile {
print_count: number;
last_printed_at: string | null;
notes: string | null;
// User link + photos of the printed result (#3077); source_url is the
// read-only import provenance (MakerWorld).
external_url: string | null;
photos: string[];
source_url: string | null;
duplicates: LibraryFileDuplicate[] | null;
duplicate_count: number;
// User tracking (Issue #206)
@@ -8096,6 +8147,11 @@ export interface LibraryFileListItem {
// matching rows on screen. 0 when the file is not grouped.
variant_group_id?: number | null;
variant_count?: number;
// Metadata indicators (#3077). Optional for the same reason as `tags`: older
// mocks construct list items without them. Read sites default to falsy.
external_url?: string | null;
has_notes?: boolean;
photo_count?: number;
}
// Variant groups (#671 / #2570): the same job sliced for different printers.
@@ -8133,6 +8189,7 @@ export interface LibraryFileUpdate {
folder_id?: number | null;
project_id?: number | null;
notes?: string | null;
external_url?: string | null;
}
// Library trash (#1008)