Adds 'Destinations' sidebar entry + view + add/edit/delete/test modal.
Generate-keypair button shows the public key for the user to paste into
the remote authorized_keys.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Foundation for the move/quarantine pipeline. Lets users register one or
more remote SFTP destinations through the API, store credentials at rest
under /data/sftp/{id}.{password|key} (mode 600), and verify connectivity
+ write access via a test endpoint.
Endpoints:
GET /api/sftp/destinations
POST /api/sftp/destinations — create
PUT /api/sftp/destinations/{id} — update
DELETE /api/sftp/destinations/{id}
POST /api/sftp/destinations/{id}/test — connect, stat base_path, mkdir probe
POST /api/sftp/keypair — generate ED25519 keypair
Host keys pinned per-destination on first connect (TOFU); subsequent
mismatches are rejected. paramiko added to requirements.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Both _folder_priority and _path_penalty were scanning the entire path
string including the basename. A file named 'mytrashed_pic.jpg' in
/photos/MobileBackup/ would falsely match the 'trash' token.
Now only directory segments are checked; filename never influences keeper
selection beyond its actual path location.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The detail-panel insertion logic mixed parent contexts: it called
grid.parentNode.insertBefore() but used a child-of-grid as the reference
node. insertBefore requires the reference node to be a child of the
target parent — it threw 'node is not a child of this node' on every
click.
Replaced the inter-row positioning with simple insert-after-grid. Same
visual outcome since panel.scrollIntoView() handles user focus.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
After every scan, automatically kick off a background thread that
generates a JPEG thumbnail for every file in a duplicate group and
caches it locally at /data/thumbs/. Idempotent — already-cached files
are skipped.
New endpoints:
POST /api/thumbs/generate — start pre-gen for all files
POST /api/thumbs/generate?only_in_groups=true — only dup-group files
GET /api/thumbs/status — progress (total/done/skipped/failed)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Thumbnails (256px JPEG, q80) generated on first request and cached at
/data/thumbs/<shard>/<file_id>.jpg — i.e. on the workstation's local SSD,
not the NAS. Subsequent requests serve straight from cache, never
re-fetching from /photos.
HEIC/HEIF decoded via pillow-heif so iPhone photos finally render.
Videos cached as a single ffmpeg-extracted frame, not regenerated each
request. New DELETE /api/thumb/cache endpoint to wipe it.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Use QUOTE_ALL + sanitise NUL/CR/LF in path/filename/exif fields. Default
csv dialect rejected fields containing line terminators with 'need to
escape, but no escapechar set'.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds a path-penalty score that downranks files in folders named Trashed,
Dups, Backup, Copy, Old, Archive, plus a penalty for repeated path segments
(e.g. Desktop/Desktop/Files) and very deep paths. Also captures and uses
file mtime as a tiebreaker — older files are usually the originals.
Applied to all four detection passes (sha256, phash, exif, filesize+dim)
and to auto-resolve-exact.
New file_mtime column with idempotent migration.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
build-deb.sh used 'cp -r app/ source/' which renames app to source
when source doesn't yet exist, dropping the app/ wrapper that the
Dockerfile's COPY app/ /app/ depends on. The 2>/dev/null || true
on the cp lines hid the resulting failures, so the .deb shipped a
broken /opt/dupfinder/source/ that build-from-source could not use.
Pre-create the source dir and copy each item to its explicit
destination path. Bump package version to 1.0.1.
Also rework dupfinder-setup.sh's image-prep step: prefer a local
image, then a quiet registry pull, then build from the bundled
source. Removes the loud registry-not-found error that scared users
when the (unpublished) tocmo0nlord/dupfinder image wasn't on Docker
Hub.
The .deb install instructions in the README pointed at a URL that
doesn't exist — Gitea exposes the Debian registry as an apt repo, not
as plain file downloads. Switched the README to the apt-repo flow
(add a sources.list line, then apt install).
Also fixed build-deb.sh: Gitea's Debian package endpoint returns
HTTP 405 for token-bearer auth; it requires HTTP basic auth (user +
token-as-password) and the literal /upload suffix on the URL.
Package built and pushed to the registry — apt install works now.
debian/control, postinst, prerm, postrm — standard dpkg package lifecycle
debian/files/opt/dupfinder/dupfinder-setup.sh — interactive setup:
checks Docker, detects NVIDIA GPU, prompts for photos/data paths,
writes docker-compose.override.yml with GPU reservation if present,
pulls image from registry (builds from source as fallback)
debian/files/usr/local/bin/dupfinder — CLI wrapper:
setup / start / stop / restart / status / logs / open / update
debian/files/etc/systemd/system/dupfinder.service — systemd unit,
guards against starting before setup has run
debian/build-deb.sh — builds .deb and uploads to Gitea package registry;
prints the exact apt sources.list line on success
Install on any Debian/Ubuntu machine:
echo "deb [trusted=yes] http://192.168.1.64:3000/api/packages/tocmo0nlord/debian bookworm main" \
| sudo tee /etc/apt/sources.list.d/dupfinder.list
sudo apt update && sudo apt install dupfinder
sudo dupfinder setup
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>