Files
chris.giteaandClaude Opus 5.5 06e396b153 feat(credits): render the credits from a single markdown file
src/main/resources/userguide/credits.md now lists every shipped library
with its license (read from the dependencies' POMs), the recognition models
and the map and place data. CreditsView, opened from the ? in Settings,
renders it through the new MarkdownPage helper (per-language loading, links
opened in the browser), which the search help popup now shares, and the
release's distrib step copies it to distrib/CREDITS.md in the same commit
as the release note.

MarkdownToBBCode learns [text](url) links, inside bold too. URLs are quoted
in the generated BBCode: AtlantaFX's parser reads a tag ending in "/]" as
self-closing, so a URL with a trailing slash broke the whole page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011xpLSeYKKHX6o16jzYLgZv
2026-09-30 15:27:51 -04:00

128 lines
7.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Pholio
Pholio is a desktop application for managing large photo and video libraries, built for volume and speed.
- **Libraries** — point Pholio at one or more folders; it indexes every photo and video into its own
embedded database, one per library, and keeps it in sync as files change.
- **Gallery** — a justified, date-grouped grid that stays fluid on tens of thousands of files, with a
full-screen photo view and an info panel for the selected file.
- **Metadata** — reads EXIF, IPTC and XMP (RAW, HEIF and video included), edits capture date, location and
rating, and writes them back to the file itself.
- **Places** — offline reverse geocoding from a bundled GeoNames dataset, plus optional online place
search (LocationIQ).
- **People** — local face and animal detection and clustering (ONNX Runtime, nothing leaves the machine);
naming a person makes their photos searchable.
- **Search** — free text, rating (`r:4+`), people (`p:Alice`) and folder (`path:Holidays/2024`) filters,
combined freely — see the in-app guide (the `?` next to the search bar) or
[`src/main/resources/userguide`](src/main/resources/userguide/search-syntax.md).
- **Command line** — the same binary runs headless: `pholio scan <dir>` reports the media files a folder holds.
Built with Java 26, JavaFX (AtlantaFX, GemsFX, Ikonli), Spring Boot, H2, PicoCLI and metadata-extractor.
## Build and run
Requirements: a JDK 26 that bundles JavaFX (e.g. Azul Zulu FX 26). Maven comes with the wrapper.
```bash
./mvnw verify # compile, test, build target/pholio.jar
java -jar target/pholio.jar --ui # desktop interface
java -jar target/pholio.jar scan ~/Pictures [-R] # headless scan; -R: do not recurse
java -jar target/pholio.jar -V # version
java -jar target/pholio.jar # usage, same as --help
```
Pholio runs headless by default: only `--ui` opens the window, so every launcher meant to show one (IDE run
configuration, installers) passes it. Any argument Pholio does not recognise is read as a command.
## Development
### JVM options (`-D…`)
| Option | Default | Effect |
|---|---|---|
| `--enable-native-access=javafx.graphics,ALL-UNNAMED` | — | Not a property, but recommended: silences the restricted-method warnings from JavaFX, JNA and the macOS process naming. |
| `-Dpholio.home=<dir>` | OS user dirs | Puts configuration, data (libraries) and cache under one root — keeps a test run out of your real profile. |
| `-Dpholio.skipTheme=true` | `false` | Starts without the AtlantaFX theme, on JavaFX's default styling (to tell a theme issue from a layout one). |
| `-Dpholio.modalBackend=dialogPane` | `stage` | Shows dialogs in GemsFX's embedded `DialogPane` instead of a separate window. |
| `-Djavafx.enablePreview=true` | set by the app | Required for the JavaFX `HeaderBar` preview API; `GuiBootstrap` sets it unless you pass a value. |
| `-Dprism.dirtyopts=false` | set by the app | Works around a stale-paint bug in Prism; `GuiBootstrap` sets it unless you pass a value. |
### Application settings (`--pholio.…=` arguments)
Spring Boot settings from `application.yaml`, overridable on the command line (they bypass PicoCLI, so they
never read as a command):
| Setting | Default | Effect |
|---|---|---|
| `--pholio.dev-tools.enabled=` | `true` | F12 opens the DevToolsFX scene-graph inspector. |
| `--pholio.library.routing.enabled=` | `true` | Routes the database to the library picked in the header; `false` pins `spring.datasource.url`. |
| `--pholio.pools.cpu=` | `0` | Threads for decoding work (thumbnails, hashes); `0` = cores − 1. |
| `--pholio.pools.scheduler=` | `2` | Scheduler threads. |
| `--pholio.window.default-width=` / `default-height=` | `1440` / `900` | First-run window size. |
| `--logging.level.org.icroco.pholio=` | `DEBUG` | Log level of Pholio's own code. |
### What makes the jar big
`target/pholio.jar` is ~223 MB, 89 libraries. Almost all of it is recognition: the application's own code is
under 1 MB.
| Size (in the jar) | Entry | Why |
|---:|---|---|
| 130.5 MB | `onnxruntime-1.21.1.jar` | ONNX Runtime with native libraries for 5 platforms (~590 MB unpacked, win-x64 alone 351 MB). The installers keep only their own platform's — see below. |
| 34.2 MB | `models/recognition/face_recognition_sface_2021dec.onnx` | Face embedding model (SFace). |
| 10.2 MB | `geodata/cities1000.txt.gz` | GeoNames seed for offline reverse geocoding, imported into H2 on first run. |
| 4.4 MB | `byte-buddy` | Runtime agent behind `@FxThread`. |
| 3.2 MB | `models/recognition/yolox_nano.onnx` | Animal detection model. |
| 3.0 MB | `gemsfx` | UI controls. |
| 2.6 MB | `h2` | Embedded database. |
| ≤ 2 MB each | Spring, Jackson, JNA, AtlantaFX themes, … | The remaining ~30 MB. |
Deliberately left out of the jar (`pom.xml`): Lombok and Jilt (compile-time only), the OpenJFX jars (every
target runtime already has JavaFX as modules), and the JUnit/AssertJ that `devtoolsfx` wrongly declares at
compile scope.
The native installers go further (`distrib/packaging/common.sh`): only the target platform's ONNX Runtime
natives (130 MB → ~24 MB for macOS arm64) and a jlink'ed runtime (~64 MB) — the macOS arm64 `.dmg` is ~157 MB.
To re-measure: `unzip -v target/pholio.jar | sort -k3 -n -r | head -20` (third column: compressed size).
### Credits
`src/main/resources/userguide/credits.md` is the single list of credits — shipped libraries with their
licenses, recognition models, map and place data. The application shows it from the `?` in Settings
(`CreditsView`), and each release copies it to `distrib/CREDITS.md`. Add a library or data source there when
you add one to the application.
### Useful scripts
- `python3 tools/icons/make-macos-icon.py` — regenerates the macOS icon, `icon.png` and `pholio.icns`
from `src/main/resources/images/spo-1024x1024.png`.
- `python3 tools/demo/fetch-commons-photos.py <dir> [--quality] [--places Paris Kyoto …] [--per-place N]` —
downloads a demo library for screenshots from Wikimedia Commons: freely licensed JPEG originals whose own
EXIF has a capture date and GPS, one folder per city, with a `CREDITS.md` (CC BY / BY-SA require the
credit wherever the photos are shown). `--quality` keeps only Commons' Quality/Featured pictures — nicer,
fewer, slower. `--help` for every option.
## Release
```bash
./mvnw -Prelease validate
```
Needs a clean working tree (or `-Drelease.allowDirty=true`) and a logged-in GitHub CLI (`gh auth login`).
In order, it:
1. bumps the version to `YYYY.M.N` — same month as the current version: `N + 1`, otherwise build 0 of the
current month;
2. runs a fresh `./mvnw verify`;
3. writes `distrib/release_note/release-note-<version>.md` from the Conventional Commits since the previous
release (grouped by type, then scope);
4. creates GitHub release `v<version>` in [Imag-In/Pholio](https://github.com/Imag-In/Pholio) with the jar,
its SHA-256 and the release note;
5. commits `pom.xml` and tags `v<version>` locally;
6. commits the release note in `distrib/` and pushes it.
The source repository itself is never pushed to GitHub: `Imag-In/Pholio` only receives release assets and
the `distrib/` worktree (an orphan branch), and a local `pre-push` hook enforces it. See `AGENTS.md` for the
details, and keep commit messages in the Conventional Commits format so they reach the release note.