diff --git a/templates/upload.html b/templates/upload.html
index 2d5fcc8..1446f84 100644
--- a/templates/upload.html
+++ b/templates/upload.html
@@ -94,8 +94,33 @@
refresh();
});
+ // Mirror of the server's sanitiseSlug, so the client can predict the slug a
+ // filename yields and keep two files in one batch from colliding.
+ function slugify(name) {
+ let out = '';
+ for (const ch of name.toLowerCase()) {
+ if ((ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch === '-') out += ch;
+ else if (ch === ' ' || ch === '_') out += '-';
+ }
+ return out;
+ }
+
+ // A slug unique among the current cards. The server derives the slug from
+ // the uploaded filename, so two photos named the same would otherwise
+ // overwrite each other; the second (third, …) gets -2, -3, …
+ function uniqueSlug(filename) {
+ const stem = filename.replace(/\.[^.]+$/, '');
+ const base = slugify(stem) || 'photo';
+ let candidate = base;
+ let n = 2;
+ while (items.some((i) => i.slug === candidate)) candidate = base + '-' + (n++);
+ return candidate;
+ }
+
function addItem(file) {
- const item = { id: ++seq, file, status: 'pending', el: null };
+ const ext = (file.name.match(/\.[^.]+$/)?.[0] || '.jpg').toLowerCase();
+ const item = { id: ++seq, file, ext, slug: '', status: 'pending', el: null };
+ item.slug = uniqueSlug(file.name); // before push, so it compares against the others
items.push(item);
item.el = buildCard(item);
list.appendChild(item.el);
@@ -177,7 +202,9 @@
setStatus(item, 'up', 'Uploading…');
const form = new FormData();
- form.append('image', item.file);
+ // Send under the deduped slug so the server (which slugs from the
+ // filename) can't overwrite another photo in the same batch.
+ form.append('image', item.file, item.slug + item.ext);
form.append('caption', item.el.querySelector('.c-caption').value);
form.append('date', item.el.querySelector('.c-date').value);
form.append('tags', item.el.querySelector('.c-tags').value);