add file thumbnails

This commit is contained in:
E44
2025-11-10 12:45:46 +01:00
parent 706e8350af
commit a247f59dd5
11 changed files with 120 additions and 11 deletions
+7 -3
View File
@@ -2,7 +2,7 @@ import { notifications } from "./stores/notification";
import { to_display_status, type DisplayStatus, type FolderElement, type TreeElement } from "./types";
import { get_uuid } from "./utils";
export async function get_screenshot(ip: string) {
export async function get_screenshot(ip: string): Promise<Blob|null> {
const options = { method: 'PATCH' };
return await request_display(ip, '/takeScreenshot', options);
}
@@ -63,8 +63,7 @@ done
if (response_element.name.charAt(2) !== '.') {
const folder_element: FolderElement = {
id: get_uuid(),
hash: JSON.stringify(response),
thumbnail_url: null,
hash: JSON.stringify(response_element),
name: response_element.name.slice(2), // remove "./"
type: response_element.type,
date_created: new Date(response_element.created),
@@ -111,6 +110,11 @@ export async function ping_ip(ip: string): Promise<DisplayStatus> {
return raw_response.status ? to_display_status(raw_response.status) : null;
}
export async function get_thumbnail_blob(ip: string, path_to_file: string): Promise<Blob|null> {
const raw_response = await request_display(ip, `/file/preview${path_to_file}`, { method: 'GET' });
return raw_response;
}
@@ -0,0 +1,20 @@
import Dexie, { type EntityTable } from "dexie";
export interface ThumbnailBlobDBEntry {
hash: string;
blob: Blob;
}
export class FileDatabase extends Dexie {
thumbnail_blobs!: EntityTable<ThumbnailBlobDBEntry, "hash">; // (Type, PrimaryKey)
constructor() {
super("FileDatabase");
this.version(1).stores({
thumbnail_blobs: "++hash, blob" // primary key
});
}
}
export const db = new FileDatabase();
+3 -1
View File
@@ -3,10 +3,12 @@ import { displays, run_on_all_selected_displays, update_displays_with_map, updat
import { ping_ip } from "./api_handler";
import type { Display } from "./types";
import { change_file_path, update_folder_elements_recursively } from "./stores/files";
import { db } from "./indexdb/file_thumbnails.db";
const update_display_status_interval_seconds = 20;
export async function on_start() {
await db.thumbnail_blobs.clear();
await update_all_display_status();
await setInterval(update_all_display_status, update_display_status_interval_seconds * 1000);
@@ -28,5 +30,5 @@ async function update_all_display_status() {
async function on_display_start(display: Display) {
await update_folder_elements_recursively(display, '/');
await update_screenshot(display.id);
await update_screenshot(display.id);
}
@@ -109,6 +109,13 @@ export async function update_screenshot(display_id: string, check_type: "first_c
const display_ip = get_display_by_id(display_id, get(displays))?.ip;
if (!display_ip) return;
const new_blob = await get_screenshot(display_ip);
if (!new_blob) {
update_displays_with_map((display: Display) => {
if (display.id !== display_id) return display;
return { ...display, preview_url: null, preview_timeout_id: null };
})
return;
}
const display = get_display_by_id(display_id, get(displays));
let update_needed = check_type === "first_check";
+11
View File
@@ -5,6 +5,7 @@ import { selected_file_ids } from "./select";
import { get_file_data, get_file_tree_data } from "../api_handler";
import { notifications } from "./notification";
import { CirclePoundSterling } from "lucide-svelte";
import { deactivate_old_thumbnail_urls, generate_thumbnail } from "./thumbnails";
export const all_files: Writable<Record<string, Record<string, FolderElement[]>>> = writable<Record<string, Record<string, FolderElement[]>>>({});
// {
@@ -30,6 +31,8 @@ export async function change_file_path(new_path: string) {
return [];
})
deactivate_old_thumbnail_urls();
for (const display_group of get(displays)) {
for (const display of display_group.data) {
const changed_paths = await get_changed_directory_paths(display, new_path);
@@ -111,6 +114,13 @@ export async function update_folder_elements_recursively(display: Display, file_
const existing_folder_elements = files[file_path].hasOwnProperty(display.id) ? files[file_path][display.id] : [];
const diff = get_folder_elements_difference(existing_folder_elements, new_folder_elements);
// Generate Thumbnails:
setTimeout(async () => {
for (const folder_element of diff.new) {
await generate_thumbnail(display.ip, file_path, folder_element);
}
}, 0)
files[file_path][display.id].push(...diff.new);
return remove_folder_elements_recursively(files, display, diff.deleted, file_path);
})
@@ -171,6 +181,7 @@ function get_folder_elements_difference(old_elements: FolderElement[], new_eleme
// export function updates_files_on_display(display_id: string, new_folder_elements: FolderElement[], file_path: string) {
// all_files.update((files) => {
// if (!files.hasOwnProperty(file_path)) {
@@ -0,0 +1,36 @@
import { get, writable, type Writable } from "svelte/store";
import { get_thumbnail_blob } from "../api_handler";
import { supported_file_types, type FolderElement } from "../types";
import { db } from "../indexdb/file_thumbnails.db";
export const active_thumbnail_urls: Writable<string[]> = writable<string[]>([]);
export async function generate_thumbnail(display_ip: string, path: string, folder_element: FolderElement): Promise<void> {
if (!Object.values(supported_file_types).some(e => e.mime_type === folder_element.type) || !folder_element.hash) return;
const hash: string = folder_element.hash;
if (await db.thumbnail_blobs.get(hash)) return;
const thumbnail_blob = await get_thumbnail_blob(display_ip, path + folder_element.name);
if (!thumbnail_blob) return;
await db.thumbnail_blobs.add({ hash: hash, blob: thumbnail_blob });
}
export async function get_thumbnail_url(hash: string): Promise<string | null> {
if (hash === null) return null;
const thumbnail_blob = await db.thumbnail_blobs.get(hash);
if (!thumbnail_blob) return null;
const new_url = URL.createObjectURL(thumbnail_blob.blob);
active_thumbnail_urls.update((current: string[]) => {
current.push(new_url);
return current;
})
return new_url;
}
export function deactivate_old_thumbnail_urls() {
const current_urls = get(active_thumbnail_urls);
for (const url of current_urls) {
URL.revokeObjectURL(url);
}
active_thumbnail_urls.update(() => { return []; })
}
-1
View File
@@ -48,7 +48,6 @@ export const supported_file_types: Record<string, SupportedFileType> = {
export type FolderElement = {
id?: string;
hash: string | null;
thumbnail_url: string | null;
name: string;
type: string;
date_created: Date;