- Python 59.8%
- JavaScript 20.4%
- CSS 9.5%
- HTML 9.4%
- Shell 0.6%
- Other 0.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .forgejo/workflows | ||
| app | ||
| deploy | ||
| scripts | ||
| tests | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| CHANGELOG.md | ||
| docker-compose.yml | ||
| Dockerfile | ||
| README.md | ||
| requirements.txt | ||
| UPGRADE_v0.1.1.md | ||
| UPGRADE_v0.1.2.md | ||
| UPGRADE_v0.1.3.md | ||
| UPGRADE_v0.2.0.md | ||
| VERSION | ||
Holland Photo Converter
Holland Photo Converter is a self-hosted TrueNAS application for safe HEIC/HEIF → JPEG conversion and exact-content folder consolidation. v0.2.0 adds a second Folder Merge tab for recursively comparing and consolidating mixed-content folders without modifying the source tree or creating new duplicate file content in the destination.
v0.2.0 at a glance
Photo Converter
- Browse folders inside
/photosand scan only the selected scope. - Recursive or selected-folder-only scanning.
- Live scan progress and Cancel Scan.
- Grid/List photo browser with search, pagination, previews, metadata, and Live Photo awareness.
- Maximum-fidelity JPEG conversion with guarded source handling.
- Versioned/no-cache application assets so upgrades do not leave stale browser JavaScript.
Folder Merge
- Separate top-level Folder Merge tab.
/filesis bind-mounted from/mnt/Holland Fast/MHolland.- Choose a Destination / Master Folder and a Source Folder anywhere beneath
/files. - Always compares the full recursive directory trees.
- Exact duplicate detection is based on SHA-256 file content, not filename or relative path.
- Detects a source file as already present even when the matching destination file has a different name or is in a different subfolder.
- Detects duplicate content within the source tree and selects only one representative copy when that content does not yet exist in the destination.
- Compare Folders is the non-destructive review/dry-run stage.
- Copy Unique Files copies only unique source content while preserving the source relative path when possible.
- Same-path/different-content collisions are preserved by choosing a safe
name (2).extstyle target instead of overwriting. - Source files are never renamed, moved, or deleted by Folder Merge.
- Copying uses a temporary destination file, SHA-256 verification, metadata/timestamp copying, and atomic commit.
- Comparison and merge operations can both be cancelled from the UI.
- A source/destination pair is revalidated immediately before copying; if either tree changed after comparison, the merge is stopped and a new comparison is required.
- Symbolic links are not followed or copied; they are reported as an issue and block the merge until reviewed.
- Persistent SHA-256 cache in SQLite avoids re-hashing unchanged files on future comparisons.
The Folder Merge tool prevents new duplicate content from being added. It intentionally does not remove duplicates that already exist inside the destination before the merge.
Safety model
Photo conversion
- Read the source HEIC/HEIF.
- Convert into a temporary JPEG beside the source.
- Verify the temporary JPEG is readable and dimensions match.
- Copy EXIF/GPS/XMP/IPTC/ICC metadata.
- Preserve configured timestamps/permissions.
- Atomically commit the verified JPEG.
- Only then apply the configured source action.
The default source action is Keep original HEIC.
Folder merge
- Recursively inventory source and destination.
- Compare file sizes to identify possible content matches efficiently.
- SHA-256 hash only the groups that can possibly match; cached hashes are reused when size/mtime/ctime are unchanged.
- Classify every source file as:
- Unique
- Already Exists in destination
- Source Duplicate
- Error
- Present the comparison for review before any files are copied.
- Immediately before merge, rescan source and destination metadata and verify that neither tree changed after the comparison.
- Create source subfolder structure as needed in the destination.
- Copy each unique file to a hidden temporary file in the destination filesystem.
- SHA-256 verify the temporary copy.
- Atomically rename the verified file to its final target without overwriting existing data.
Cancelling a merge leaves files already completed in the destination and leaves the entire source unchanged. Run Compare Folders again before continuing after a cancelled merge.
HollandNAS layout
Development working copy in code-server:
~/Holland_Photo_Converter
Forgejo repository:
Testing/Holland-Photo-Converter
Forgejo image registry:
github.hollandayinn.com/testing/holland-photo-converter:<version>
TrueNAS mounts for v0.2.0:
/mnt/Holland Fast/photo-converter/data -> /data
/mnt/Holland/HollandArchive/Photos -> /photos
/mnt/Holland Fast/MHolland -> /files
Optional archive dataset:
/mnt/Holland Fast/photo-converter/originals -> /archive
/data stores the SQLite database, settings, history, preview cache, folder-merge comparison records, and persistent hash cache.
Folder Merge examples
Suppose the destination contains:
/files/Folder1/Documents/Resume.docx
and the source contains the exact same bytes here:
/files/Folder2/Test/Matt Resume 2026.docx
The file is classified Already Exists and is not copied because the SHA-256 content matches, despite both the filename and subfolder being different.
If the source contains:
/files/Folder2/New/Manual.pdf
and that content does not exist anywhere in the destination, it is copied to:
/files/Folder1/New/Manual.pdf
If /files/Folder1/New/Manual.pdf already exists with different content, the existing file is preserved and the incoming file receives a collision-safe name such as:
/files/Folder1/New/Manual (2).pdf
Scanner behavior
The Photo Converter scanner remains constrained to /photos. A selected scan location is stored as a relative path and every request is validated against that root. Cancelled scans preserve everything indexed so far and deliberately skip missing-file reconciliation.
The Folder Merge browser is separately constrained to /files; it cannot browse outside the /mnt/Holland Fast/MHolland bind mount.
Source handling for photo conversion
Keep — default
Creates IMG_1234.jpg beside IMG_1234.HEIC and leaves the HEIC untouched.
Archive
Moves the HEIC to the corresponding path under /archive only after a verified conversion. Archive mode requires a dedicated /archive bind mount.
Delete
Deletes the original only after verified conversion and explicit authorization. Folder Merge does not use Photo Converter source-handling settings and never deletes its source files.
Development and release workflow
Edit in code-server, then:
./scripts/validate.sh
git status
git add .
git commit -m "Holland Photo Converter v0.2.0"
git push
Forgejo uses the existing workflow:
Validate -> holland-builder
Build Image -> holland-builder + holland-ci:1.0.1
The build workflow publishes:
github.hollandayinn.com/testing/holland-photo-converter:0.2.0
github.hollandayinn.com/testing/holland-photo-converter:<commit-sha>
github.hollandayinn.com/testing/holland-photo-converter:latest
TrueNAS Custom App image
Deploy:
github.hollandayinn.com/testing/holland-photo-converter:0.2.0
Use deploy/truenas-custom-app.yml from this release. The important new v0.2.0 mount is:
- "/mnt/Holland Fast/MHolland:/files"
Existing /data SQLite state migrates in place. No manual database migration is required.
Validation
Static validation:
./scripts/validate.sh
Regression smoke tests when Python app dependencies are installed:
python3 tests/smoke_v011.py
python3 tests/smoke_v012.py
python3 tests/smoke_v013.py
python3 tests/smoke_v020.py
smoke_v020.py verifies recursive cross-folder duplicate recognition, duplicate-source collapse, collision-safe copy naming, exact-content merge behavior, and that the source tree remains byte-for-byte unchanged.