# 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
` 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=` | 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 [--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-.md` from the Conventional Commits since the previous
release (grouped by type, then scope);
4. creates GitHub release `v` 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` 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.