Add configurable formats and lazy decoders, v0.7.1

This commit is contained in:
dmo committed 2026-07-23 21:51:37 +02:00
1 parent b9a0259166
commit 8ed5490afc
8 files changed
+193 -35

No files matched your search

+39 -15
View File
@@ -13,6 +13,9 @@ const state = {
mapMarker: null,
locationDirty: false,
favoriteLocations: [],
configPromise: null,
enabledExtensions: new Set(),
enabledFormatLabel: '',
};
const $ = (selector) => document.querySelector(selector);
@@ -128,6 +131,12 @@ function requiresServerPreview(photo) {
return photo.format === 'TIFF' || photo.format === 'HEIF' || photo.format === 'DNG';
}
function isEnabledFilename(filename) {
const dot = filename.lastIndexOf('.');
if (dot < 0) return false;
return state.enabledExtensions.has(filename.slice(dot).toLowerCase());
}
function storageKey() {
return state.directoryHandle ? `photo-date-editor:${state.directoryHandle.name}` : null;
}
@@ -154,14 +163,13 @@ function saveFolderState() {
}
async function openFolder() {
await loadFavoriteLocations();
if (!isSupported()) {
showToast('Use a current Chromium browser over HTTPS. Folder access is unavailable here.', true);
return;
}
try {
await loadAppConfig();
await loadFavoriteLocations();
if (!isSupported()) {
showToast('Use a current Chromium browser over HTTPS. Folder access is unavailable here.', true);
return;
}
const handle = await window.showDirectoryPicker({ mode: 'readwrite', id: 'photo-date-editor' });
const permission = await handle.requestPermission({ mode: 'readwrite' });
if (permission !== 'granted') throw new Error('Read/write permission was not granted.');
@@ -179,7 +187,7 @@ async function scanFolder() {
const photos = [];
for await (const [name, handle] of state.directoryHandle.entries()) {
if (handle.kind !== 'file' || !/\.(jpe?g|png|tiff?|heic|heif|hif|dng)$/i.test(name)) continue;
if (handle.kind !== 'file' || !isEnabledFilename(name)) continue;
const file = await handle.getFile();
const previous = stored.photos?.[name] || {};
const unchanged = !previous.lastModified || previous.lastModified === file.lastModified;
@@ -218,7 +226,7 @@ async function scanFolder() {
} else {
elements.viewerContent.classList.add('hidden');
elements.viewerEmpty.classList.remove('hidden');
showToast('No JPG, PNG, TIFF, HEIC, HEIF, HIF, or DNG files were found in that folder.', true);
showToast(`No enabled ${state.enabledFormatLabel} files were found in that folder.`, true);
}
}
@@ -1026,18 +1034,34 @@ window.addEventListener('keydown', (event) => {
else if (!typing && event.key.toLowerCase() === 'c') copyPreviousValues();
});
async function initialise() {
try {
const response = await fetch('/api/config', { cache: 'no-store' });
if (response.ok) {
function loadAppConfig() {
if (!state.configPromise) {
state.configPromise = (async () => {
const response = await fetch('/api/config', { cache: 'no-store' });
if (!response.ok) throw new Error(`Configuration request failed (${response.status}).`);
const config = await response.json();
const extensions = Array.isArray(config.enabledExtensions) ? config.enabledExtensions : [];
if (!extensions.length) throw new Error('The server has no enabled photo formats.');
const pageTitle = config.appTitle || config.appName;
elements.appName.textContent = pageTitle;
elements.appSubtitle.textContent = config.appSubtitle;
document.title = pageTitle;
state.mapConfig = config;
}
} catch {}
state.enabledExtensions = new Set(extensions.map((value) => String(value).toLowerCase()));
state.enabledFormatLabel = config.enabledFormatLabel || 'photo';
return config;
})();
}
return state.configPromise;
}
async function initialise() {
try {
await loadAppConfig();
} catch (error) {
setSaveStatus('failed', 'Configuration unavailable', error.message || 'Could not load enabled formats.');
for (const button of elements.openButtons) button.disabled = true;
}
// Favorite locations are server-side shared data and must be loaded on every page start,
// independently of whether the user opens a photo folder in this browser session.
+2 -2
View File
@@ -16,7 +16,7 @@
<div class="brand-mark" aria-hidden="true">◫</div>
<div>
<h1 id="app-name">Photo Date Editor</h1>
<p id="app-subtitle">Local folder · in-place JPG, PNG, TIFF, HEIF and DNG metadata</p>
<p id="app-subtitle">Loading enabled photo formats…</p>
</div>
</div>
<div class="top-actions">
@@ -74,7 +74,7 @@
</select>
</div>
<div id="file-list" class="file-list" aria-live="polite">
<div class="empty-list">Choose a folder containing JPG, PNG, TIFF, HEIC, HEIF, HIF, or DNG photos.</div>
<div class="empty-list">Choose a folder containing an enabled photo format.</div>
</div>
</section>
+1 -1
View File
@@ -1,4 +1,4 @@
const CACHE_NAME = 'photo-date-editor-v7-0';
const CACHE_NAME = 'photo-date-editor-v7-1';
const SHELL = ['/', '/styles.css', '/app.js', '/manifest.webmanifest', '/vendor/leaflet/leaflet.css', '/vendor/leaflet/leaflet.js'];
self.addEventListener('install', (event) => {