This release includes a major database schema migration (v2 to v3). The migration runs automatically on first launch and includes:
- New
DirUritable for normalized directory storage (reduces database size) - New
TagHierarchytable for faster tag tree queries - New
RejectedFiletable for tracking why files were skipped - Removal of dominant color columns (
mode0-mode6) fromAssetFiletable
Note: Dominant color extraction has been removed from asset aggregation. The feature added complexity without sufficient benefit for asset matching, and the color similarity search was rarely used.
The --write-settings CLI argument has been renamed to --init. Update any scripts that use this flag.
PhotoStructure now requires Node.js v24.x LTS. Node.js v21.x and v22.x are end of life.
PhotoStructure now uses just the $PATH environment variable and the (optional) PS_TOOLS_PATH setting to find external tools. Individual tool path settings (ffmpegPath, dcraw_emuPath, heifConvertPath) have been removed. Read more on Discord.
Video transcoding settings have migrated from bitrate-based to CRF-based (Constant Rate Factor) encoding. CRF provides more consistent quality across different video content. If you had custom videoBitrate settings, configure the new transcodeCrf setting instead.
For server editions, the new photostructure fix command runs maintenance and validation jobs:
fix --tags: Validate tags, rebuild search index, recount assetsfix --db: Run VACUUM, ANALYZE, and integrity checks (auto-repairs if corruption detected)fix --db-backup: Create a backup of the library databasefix --cleanup: Kill orphaned processes, remove stale files
PhotoStructure now tracks a hash of all Filters settings. When filter settings change, affected files are automatically re-evaluated during the next sync. This ensures your library stays consistent when you adjust file inclusion/exclusion rules.
- Child tag dropdown: New dropdown in the tag gallery header showing all direct child tags for quick navigation
- Thumb row controls: New
+/-buttons to control how many rows of thumbnails to show
Added "county" support for geographic tagging to help discriminate between same-named cities. The default tagGeoTemplate is now ["Country","State","County","City"]. To restore the prior format, set tagGeoTemplate=["Country","State","City"] in your library's settings.toml.
PhotoStructure now looks for album.xmp and metadata.xmp files in each directory and applies their metadata as low-priority sidecars to all files in that directory. See the directorySidecars setting for details. Note: filenames are case-sensitive and sidecars are not inherited by child directories.
New keywordDashDashDelimiters setting for filename "dash-dash" keyword extraction. This lets you retain whitespace for multi-word keywords (like /photos/--/Places|United Kingdom/P437289.JPG). The keywordDelimiters setting now only applies to metadata-encoded tags.
PhotoStructure now analyzes codec/container compatibility to choose the fastest transcoding strategy:
- Remux (lossless, seconds): When codecs are browser-compatible but container isn't (e.g., MKV with H.264+AAC to MP4)
- Audio-only: When video is compatible but audio needs transcoding (e.g., MTS with H.264+AC-3)
- Video-only: When audio is compatible but video needs transcoding
- Full: Only when both streams need re-encoding
This can reduce transcode time from minutes to seconds for compatible content.
Video transcoding now detects and correctly handles colorspace metadata:
- HDR content (BT.2020/PQ/HLG) is preserved without conversion
- BT.709 (HD) content is tagged correctly
- BT.601 (SD) content is converted to BT.709 for consistent browser playback
- Unknown metadata uses height-based heuristics
Transcoded videos now use libx265 (CRF 28) for 25-40% smaller files vs H.264. Configure with transcodeVideoCodec and transcodeCrf settings.
MKV and MTS containers now correctly detect codecs via ffprobe fallback when ExifTool metadata is incomplete.
- ffprobe integration: Video metadata is now enriched by
ffprobe(if installed), providing per-stream metadata and supporting many more video formats than ExifTool alone - HEIF/HEIC thumbnail optimization: Thumbnails are now generated by
heif-thumbnailer(if available), rendering in 10-100ms instead of 2-8 seconds withheif-convert - ImageDataHash aggregation: ExifTool's ImageDataHash is now used to ensure assets differing only in metadata get correctly grouped together. See the
imageDataHashTypesetting
- Added support for Ubuntu 24.04
- Improved soft-delete (trash) support on all platforms, with proper Docker support. Read about Docker configuration
fileSizeEpsilon/mtimeMsEpsilon: Control howsyncdetects changed filesexactFitResolutions: Ensure previews fit within specific resolutions (useful for Chromecast)logRetention: Configure automatic log file cleanupsessionTimeout: Control web UI session durationdbWalAutoCheckpointPages: Advanced SQLite WAL checkpoint tuningrejectedFileCacheThresholdMs: Performance tuning for rejected file caching
- Added
$TZ,$PUID, and$PGIDto environment variable health checks (Docker) - Added shutdown link from the health page for graceful shutdown when library is unhealthy
- New file watcher health check with platform-specific limits
- Improved OS & CPU architecture detection
- ISO token in
assetPathnameFormat: Fixed file copies failing when filenames included:from ISO timestamps. The defaultISOtoken now usesyyyy-MM-dd'T'HH-mm-ss.SSS, and invalid filename characters are replaced with_ PS_FORCE_LOCAL_DB_REPLICA: Fixed setting being ignored in some situations- UNIQUE constraint on AssetFile: Fixed
UNIQUE constraint failed: AssetFileerrors, including support for in-place URI upgrades topslib:andpsfile:schemes - Health check timeouts: Fixed spurious failures from incorrect timeout closure boundaries
- SQLite fts5 integrity: Upgraded SQLite (now 3.51.1) which fixes a regression where
fts5indexes could cause integrity checks to fail - Asset visibility in sync: Sync now verifies that an asset is marked as "shown" before skipping re-processing
- Duplicate
#idattributes: Fixed duplicate HTML IDs on the settings page
- New bootstrap system: Replaced shell-based
start.shwithbootstrap.jsfor more reliable upgrades. Both./start.shand./photostructurenow work identically --reinstallflag: Pass to./photostructureto re-download and recompile third-party libraries- macOS Homebrew fix: Automatically runs
brew install -q python-setuptoolswhen needed to solve installation issues
PhotoStructure now reads from /.psenv, $HOME/.psenv, and $PS_ENV_FILE with simple key=value parsing. No more conditionals, variable expansion, or export required. Last value wins. See documentation.
- platform-folders replacement: Native dependency replaced with equivalent TypeScript
- Tag gallery PRNG: New random number generator validated with dieharder, with faster SQL
ORDER BYand shorter seeds renice()behavior: No-op whenPS_PROCESS_PRIORITY=NORMAL- In-memory task queue: Task management moved from database table to memory, simplifying code and improving performance
- JSON de-cycling: New custom format that doesn't require
eval - POSIX uid/gid handling: Properly handles platforms without numeric user/group IDs (Windows)
PS_LOG_LEVELtoken format: Now supports[TOKEN:]LEVEL,...for context-specific log levelsPS_OPT_OUT/PS_NO_NETWORK: Either setting now disables the version health checkPS_AUTO_UPGRADE_SETTINGS: Set tofalseto disable automatic settings.toml upgradesvolumeUuidFilePathschange: RemovedSystem Volume Information/IndexerVolumeGuidfrom defaults (requires admin privileges on Windows)- defaults.env cleanup: No longer suggests camelCased versions of transient settings
- fs-extra removal: Migrated to native
node:fsAPIs - Ubuntu 20.04 compatibility: Rebuilt linux-x64 and linux-arm64 tools to fix libc errors
- Improved emoji: Better emoji for "Share basic installation information?"
- Updated to Node.js v24 LTS (bookworm-slim base image)
- SQLite 3.51.1: Latest SQLite with performance improvements and bug fixes
- LibRaw 0.22: Updated RAW image processing library with new camera support
$PS_LIBRARY_DIRand$PS_CONFIG_DIRrespected when determining default$PUIDand$PGID$PUID/$PGIDrespected when spawning shells into containers- Set
$PS_NO_PUID_CHOWN=1to skipchowncalls on library files