2022 PhotoStructure release notes
Note that these are notes for versions released in 2022.
Please see the current release notes.
v2.1.0-alpha.7
π There were some crash-bugs in alpha.4, alpha.5, and alpha.6. π¬
PhotoStructure for Desktops on macOS is still not working with this build, but all other editions of PhotoStructure seem to be functional. I’ll get macOS back as soon as I can.
Please report on Discord if you see anything odd, unexpected, or possibly buggy.
π PhotoStructure for Docker users: If your docker or docker-compose scripts used
$UID, please switch to using$PUID. If you used$GID, please switch to$PGID.Prior releases tried to be “nice” and support both
$UIDand$PUID(as well as both$GIDand$PGID), but this turned out to be a bad idea.bashand other commands consider$UIDand$GIDto be reserved, read-only, and trustable environment variables, which could cause issues. We’ll just stick with the linuxserver.io standard. Details: </go/pgid>β¨ Support for remote TCP GELF-compatible logging servers via the new
PS_LOG_SERVERandPS_LOG_SERVER_LEVELsettings.β¨/π¦ “Friendly” duration strings are now supported (after I made my fourth typo in ISO duration strings). See </getting-started/advanced-settings#duration> for details.
β¨/π¦ Prior versions of PhotoStructure compiled the front-end javascript against an ES5 target, which caused older, unsupported iOS devices to not render the frontend. When we heard that Nighthawk’s Grandma’s iPad didn’t work, though, this had to be fixed. We know build against ES3, and should support ancient versions of Safari.
β¨/π¦/π The prior build (
alpha.2andalpha.3) introducedglobs, but having bothscanPathsandglobsresulted in confusion (and several bugs). File exclusion patterns were completely revisited in this build. Implementation details and usage are explained in this forum post.β¨/π Sidecar handling was improved:
photo.JPEGnow matches up withphoto.JPG.xmp.β¨ When PhotoStructure copies files on macOS and Windows, it now retains file “birthtime” metadata. This isn’t a field that exists on standard Linux filesystems, so it’s not supported there. Set
retainFileBirthtimes=falseto disable this new behavior.β¨ Lazy loading is now configurable, via the new
lazyLoadExtraVhsetting. Use a smaller value if you’re serving your library over a constrained network.β¨/π Several sync report improvements:
The sync report directory can be opened via the nav menu (if accessed via localhost), or on PhotoStructure for Desktops, via the tray and system menus.
The README.txt now includes a comprehensive list of “states” for files and directories.
Added a new “at” column that’s ISO-date-time formatted, because most spreadsheet apps don’t know how to parse millis-from-common-epoch.
Sync reports no longer worryingly state that all sidecars were skipped–the sync report now states what file(s) the sidecar will be associated with, and only marked as excluded if they don’t match with any sibling photo or video file.
Sync reports now include a “started” state emitted after being dequeued from the work queue.
Prior sync report CSVs could contain a “details” cell that included newlines. Although Excel and LibreOffice parse these CSVs properly, Google Sheets don’t, and there was discussion asking if these newlines could be avoided. Good news, everyone,
/\r?/n/gis replaced with": "in the details column now!If automatic organization is enabled (see the
copyAssetsToLibrarysetting), a new sync report row will be added when photos and videos are copied into your library.
β¨/π When sync finishes for a given path, and
retryEnqueuedistrue,syncwill look at the last day of sync reports for paths that are “stuck”–paths that have a “enqueued” entry, but no subsequent “synced”, “timeout”, or “failed” entry, and retry them.π A bug in URI root encoding caused
alpha.2throughalpha.4to have several sync reporting and progress panel-related errors, which should now be resolved.π Depending on how PhotoStructure was shut down, the
syncprocess could have been force-killed while still closing the database, which would result inSQLITE_CORRUPT. A newsyncExitTimeoutMssetting has been added, which defaults to 1 minute–this should be enough to close SQLite even on the slowest remote HDDs and largest libraries, but now you can extend this if you must.`π Work-in-progress files, (hidden files starting with
.WIP-), used by metadata extraction and transcoding ops, now use a filesystem mutex to avoid race conditions (this caused random import failures on high-CPU-count servers).π Filesystem watches for the same path are now shared within a given process (Node.js quietly fails when watch is invoked more than 3 times for the same path. Again, this would only impact users with high-CPU-count servers).
π Videos and images are no longer considered for aggregation (to avoid spurious live photo matches). (PhotoStructure will revert this when Live Photos are properly aggregated).
π PhotoStructure for Desktops has a “pre-flight check” of the library directory at startup. Prior versions could fall into an infinite loop if the directory permissions were wrong.
π Library directory suggestions now filter out any directories that are not read-write by the current user.
π The “π© Not Connected” dialog no longer flashes epi(lepti)cally when the library server isn’t available.
π Backend state, liked current running version, isPaused, and current plan is now synchronized with the front-end after every XHR request.
π Fixed
./photostructure main --tail(prior versions would erroneously report the arg as being invalid).π PhotoStructure for Desktop billing links now open in the current window
π Due to an unclosed http response, the webserver would hang if previews were missing
π Fixed
includedPreviewTagssetting’scapturedAtandexposureSettingssupport for non-standard tag locations. Preview images are tagged to let Apple Preview and Eye of Gnome properly extract exposure settings.π/π¦ Reduced db mutex contention during backups by pausing work item dequeues–this could cause tasks to “time out” when they were just waiting for the backup to complete.
π The “your library is already open” and “your library is missing” preflight check dialog buttons for PhotoStructure for Desktops didn’t work properly. We use the new electron API properly now.
π File copies could erroneously timeout under heavy I/O, causing larger file imports to fail randomly. We now use a progress watchdog instead of a hard timeout.
π/π¦ Wrapped PhotoStructure for Desktops launch block in a try/catch to ensure errors got rendered to a user-visible dialog
π¦ PhotoStructure for Docker: If
$PUIDor$PGIDaren’t “effective” (either the current effective user id doesn’t match$PUID, or current effective group id doesn’t match$PGID), all commands (main,sync,web, …) will now emit a warning with a link to the forum post with the solution: https://forum.photostructure.com/t/1597/2.π¦ Process shutdown was refactored a bit:
To avoid premature shutdown (and dreaded
SQLITE_CORRUPTerrors), we now fully rely on “endable” component timeouts, rather than having a single top-level timeout. If, say, the db takes a while to shut down, the new code will be patient and wait for it now. Shutdown may take a bit longer, but in testing I haven’t measured slower shutdowns.Child processes now listen on
stdoutfor--exit, process signals, and the newexitshared-state event to initiate shutdown.
π¦ Volume and mountpoint parsing now uses the
validateMountpointssetting to only return user-rXdirectories.π¦ Expired subscription licenses are now auto-refreshed after upgrading to a new major/minor version.
π¦ Multiprocess state sharing is now lockless to avoid multi-process deadlocks in
alpha.3.π¦ Advisory locks use filesystem mutexes, rather than relying on SQLite unique constraints.
π¦ Improved
info --exclude-globsoutputπ¦ File SHAs are cached and invalidated only if
fs.StatssizeormtimeMschange. Prior versions would invalidate previously-cached SHAs too aggressively, which could result in the entire file being re-read several times unnecessarily.π¦ Pulled in latest versions of Electron, sharp, node, TypeScript, ExifTool, and other third-party libraries.
v2.1.0-alpha.3 (server)
- π This version was only released on server editions, and fixes the
suerror indocker-entrypoint.sh.
v2.1.0-alpha.2 (desktop)
β¨ A separate, native Apple Silicon build is now available for macOS users (we didn’t go with a universal, or “fat” build, as the “thin” builds are twice as fast to download and take up half of the disk space–a universal build would have been close to 500MB, uncompressed!).
β¨ PhotoStructure now supports powerful “include” and “exclude” patterns via the new
globssetting. This replaces the priorneverIgnoredsetting.β¨ The asset header now supports direct downloading of the original asset
π On Linux and macOS,
syncno longer walks into nested mountpoints (this broke sync status and post-sync cleanup operations, like detecting deleted files).π Fixed Windows launch bug
%SYSTEMROOT% not set. PhotoStructure now uses case-insensitive environment key lookups on Windows.π Fixed Windows
missing Z:\proc\cpuinfofatal error.π On macOS, the default Apple Photos library is now appended to the “include” glob patterns.
π
ffmpeg’ssinglejpegsupport was dropped in new builds. Adjusted PhotoStructure’s frame extraction command to suit.π Several web security settings were changed. See the forum for details.
π¦ The
trustProxysetting default was changed fromfalsetoloopback. If you use PhotoStructure via a reverse proxy, please refer to the documentation associated to the setting, visit http://expressjs.com/en/guide/behind-proxies.html, or ping us on Discord for help.π¦ The
upgradeInsecureRequestssetting defaults tofalse. If anyhttpsrequest is detected, however, PhotoStructure changes the default totrue. If you access your library via bothhttpandhttps, explicitly set this setting tofalse.π¦ The
enableWebSecuritysetting was confusing, and was deleted.π¦ A new
disabledHelmetMiddlewaresetting supports configuration of Helmet.π¦ All web security settings are now all gathered in a new
Securitycategory
π¦
logtailnow accepts a log directory to tail recursivelyπ¦ Docker multistage builds took 40+ minutes on GitHub Actions. A new cached base image speeds up rebuilds to be just a minute or two.
π¦ The
infotool now lists all.JSON,.XMP,.MIE, and other sidecar files for the file(s) being examined. See the forum for details.π¦ File watching is now debounced and can squelches stat changes if the SHA doesn’t change. See the new
watchDebounceMssetting for details. This fixes the mountpoints watcher from declaring “detected change in /proc/mounts” every minute on linux systems.π¦ A bunch of settings housecleaning:
- The
VolumesandFilescategories were merged into a newFilesystemcategory - The
Timeoutscategory was deleted, and contents moved into proper categories (likemaxSyncFileTimeoutMsmoved toSync) - A new
Securitycategory was added (see above) maxEmbeddedBuffermoved toPreviews
- The
π¦
./photostructure info --cleanup(whose process is normally performed automatically bysync) now vacuums stale image caches, readdir caches, shared state, previews, advisory locks, and logfiles. Add--infoto see what it’s doing.π¦ Third-party tools were rebuilt, and compilation instructions were added as READMEs.
v2.1.0-alpha.1
We’re skipping a release of 2.0, as the changes in this release are substantial enough to return to “alpha” status.
A lot has changed behind the scenes: we refactored process scheduling to address sync and database issues several users reported, and added better sync visibility through the new sync reports.
Note for PhotoStructure for Servers users: sync-file is no longer an available command. Prior commands that used sync-file should switch to using sync.
The sync process now supports --progress, which exposes real-time import progress.
β¨ Sync imports are faster, especially for larger libraries.
The
syncIntervalHourssetting was renamed tosyncNewIntervalHours. This setting ensures detection of new photos and videos happen daily (by default).A new
syncChangedIntervalHourssetting, which defaults to weekly, detects changes made to previously imported photos or videos.Typical syncs require far less disk I/O, as they only have to
readdirevery directory, notstatevery file.System profiling identified several hotspots, including tag recounting, which has been optimized (the 10-15s process now completes in under a second)
DB vacuuming, tag, and search maintenance is now rate-limited with dynamic TTLs based on the size of the library
β¨ New sync reports are now emitted into
$library/.photostructure/sync-reports/. See the forum post for more details.
β¨/π Deduplication improvements:
If the file extension matches, we respect the millisecond captured-at precision. If the file extension doesn’t match, the precision minimum is set to 1 second, as some DSLR encode RAW/JPEG pairs with slightly (< 1s) different captured-at times.
The
imageIdandcameraIdEXIF UID values now support “synonym groups”, likeImageNumber/ShutterCountandCameraSerialNumber/SerialNumber.cameraId,imageId, andlensIdtag synonyms are now coalesced (Nikon usesImageNumberon JPG andShutterCounton NEF).Lens matching now uses a normalized lens information value. This allows for Nikon JPG/RAW pairs to be matched correctly. (Nikon’s latest bodies encode a different value for the same lens when looking at
.NEFvs.JPG).Bogus
ShutterCount:1and0000000tag values are ignored.
β¨ New
assetPathnameFormatsetting to customize automatic organization, which supportsBASE,NAME,EXT,PARENT, andISOtokens. See this forum post for more details.β¨ New filter setting
respectFileExtensions: Normally PhotoStructure uses file extensions (like.JPEGor.MP4) to perform initial file filtering, which is much faster than having to open and examine the initial bytes of every file. If you have files that don’t use valid file extensions, you can set this to false, but know that file imports will be much slower.β¨ New filter setting:
maxVideoDurationSec, the maximum number of seconds that a video can be and still be imported. If this is set to 0 or unset (the default), no maximum duration limit will be applied.β¨
syncnow supports--progresswhich shows the real-time status of every concurrent file import. Note that this mode requires an ANSI-color terminal.β¨
infonow has--cleanupand--recount-all-tagsswitches to manually run periodic maintenance tasks, including tag count updates, search index rebuilds, and database optimization, vacuuming, verification, and backup. These tasks are normally done bysync.β¨
infonow has--mountpointsto show… mountpoints.β¨
info --filtersupports “deep” value picks, likeinfo --filter "paths.libraryDir", which can be handy with--flat.β¨
info --validatesupports command-line file validation.β¨ New “easy mode” for Docker bind-mounts.
β¨ New “quick (and dirty) mode” for Raspberry Pis. RPi detection and licensing were improved (and works within Docker now).
β¨ Automatic sync throttling: when importing assets on slower disks and servers with many (8+) cores, imports can lead to PhotoStructure hitting disks “too hard” and the import process can get “stuck”. PhotoStructure will now automatically throttle back concurrency to approach
maxConcurrentImportsWhenRemoteas we get disk I/O timeouts, to try to avoid hammering disks.β¨ “Re-sync this asset” now looks for deleted, rejected, or filtered files and removes those references from the synced asset.
β¨ Support excluding photos and videos tagged with specific keywords with the new
keywordBlocklistsetting: see the forum for details.β¨ Newer versions of Firefox and Chrome don’t like non-https websites with CSP and CORS headers: PhotoStructure will automatically disable those headers for PhotoStructure for Desktops, or if
exposeNetworkWithoutAuthisfalse, but you can specify the correct setting with the newenableWebSecuritysetting.β¨ All settings ending in
Ms(for Milliseconds) andDurationnow accept ISO 8601 duration strings, “friendly” durations, as well as numeric values which will be interpreted as milliseconds. See </getting-started/advanced-settings#duration> for details.β¨ Stable inferred tags for library copies. PhotoStructure uses “sibling” files to backfill missing metadata. When photos and videos are copied into your library, there may not be siblings to restore the “inferred” metadata, and that could cause issues with tagging and imports.
This version will write inferred tags to the
HistoryXMP tag, so when metadata is missing in your library assets, PhotoStructure can recover that prior inference work.β¨ The new
defaultCopyright(which is disabled by default) gives a default value to theCopyrighttag.β¨ Preview images can now retain original metadata. Prior versions of PhotoStructure would strip all metadata from preview images to speed up rendering. The new
includedPreviewTagssetting (which defaults toAttributionName,AttributionURL,capturedAt,Copyright,License,Make,Model,Permits,Prohibits,Requires,Source, andUseGuidelines) will use a few more bytes for every preview image, but avoid metadata-less images. Set this setting to""to disable this feature.β¨ The new
writeSourceTagToLibraryCopiessetting (which defaults tofalse) will write a sidecar containing theSourcetag for all new files copied into your PhotoStructure library whose value is the full native path to the source file.β¨ Setting
cpuLoadPercentto1or0now puts sync into “single-threaded mode”, which minimizes forking and memory consumption.β¨ New
strictDeduping“meta” setting requires exact-match captured-at values (changing this value requires a library rebuild to re-aggregate your assets). It also enablesuseImageHashes, and cranks upminExposureSettingsCoeffPctto 98,minImageCoeffPctWithExactDateto 95,minImageCoeffPctWithFuzzyDateto 95,minGreyscaleImageCoeffPctto 95,minColorCoeffPctto 95,minMeanCoeffPctto 95,modeCorrCieDiffWeightto 1, andmodeCorrIndexDiffWeightto 1β¨/π/π¦ Fixed/improved timezone handling: See the update to exiftool-vendored for details.
β¨/π¦ Several additional lenses are now properly parsed, including Nikon VR and ZEISS Batis glass.
β¨/π¦ The
mainandinfoservices verify thatPS_*environment variables are known PhotoStructure environment variables. If any incorrect settings are found, the closest-named setting is suggested. This is only a warning emitted tostdout.β¨/π¦ Docker containers can now safely bind-mount
/tmpto/ps/tmp: PhotoStructure will automatically add a subdirectory, chmod’ed to700, when running in Docker. See this forum post for details.β¨/π¦ Added
.envsupport viaPS_ENV_FILE: read more hereβ¨/π¦ File path to URI construction is more reliable, delegating to previously cached volume metadata.
β¨/π¦ Added Next/Back buttons to the settings page
β¨/π¦
logtailandlogcatare dramatically faster for very large inputs, and handle stream buffering gracefully (handy if you pipe contents throughless)β¨/π¦ Deprecated settings (currently
scanMyPictures,assetSubdirectoryDatestampFormat, andsyncIntervalHours) are now automatically migrated to the setting that replaced them (if those settings are unset).π Restored the title bar on PhotoStructure for Desktops’s About page
π Progress panels on the home page are now restored.
π A new database migration was added to unset any invalid
Asset.excludedAtorAsset.deletedAtcolumn values, avoiding spuriously-removed or deleted assets.π CSP directives had to be adjusted due to new Chrome
form-actionenforcement. See thecspDirectiveandcspReportOnlysettings for details.π Zoom widgets aren’t hidden on touchscreen laptops anymore
π Improved is-file-deleted detection (volume SHAs are now used in addition to native paths to ensure we’re referencing the same path)
π GIO volumes are now properly extracted on Ubuntu 20+.04.
π Fixed zooming into rotated non-JPEG images (prior versions could incorrectly rotate image-actual)
π/π¦ Cleaned up network error message “toasts” to be consistent.
π/π¦ Work to reduce
SQLITE_BUSYerrors:syncnow uses threads rather thansync-fileprocesses, and onlysyncreads and writes to the library database. This results in more work done by thesyncprocess, but all CPU-intensive work (like image validation and preview generation) is offloaded to threads and a new childworkerprocess., and overall sync throughput should be higher, especially on high-core machines and larger libraries where write contention can wedgesync.π/π¦ Photos and videos copied into the library are now both checked for previously-existing SHA clones both by using the library and by looking at directory ancestors’ files that contain common core filename basenames.
π/π¦ The “Skipping to first non-empty child tag⦔ tag only shows once for a specific tag redirect (thanks for the suggestion, Aidan!)
π/π¦ Work to prevent DB corruption:
Database janitorial work is now only done by
syncmaindoesn’t open DB connections anymoreDB backups are only done by
syncCode that could have resulted in a partial DB replica copy now uses the work-in-progress file copier
π/π¦ System profiling found that
readdirwas a hotspot, but the prior caching approach overwhelmed the garbage collector. The new cachingreaddiravoids filesystem caching ifreaddir()returns quickly, resulting in a 10x speedup (!!)π/π¦ The
rpcPortsetting (and all inter-process RPC ports) were deleted. All shared state between processes is now coordinated via a new$config_dir/shared-state.jsonfile, where$config_diris the same directory that stores system settings. This both simplifies the codebase and allows any process to broadcast persistent or transient events at any time.π/π¦ Sidecar matching has been improved to unicode-normalize strings and match file copies with numeric copy suffixes
π¦
sync-filehas been removed, as it is no longer used bysync. Manual file and directory imports can be done via thesynctool.π¦
MetadataDatewas removed from the default set of “captured-at” tags, as this tag encodes the last time metadata was edited, not the time that the asset was captured.π¦ Volume metadata is now cached on the filesystem (in both the library and the system config directory) to let PhotoStructure handle kernel hiccups where volume metadata goes missing (like with macOS after suspend, or Windows when it feels sad).
π¦
PS_MOUNTPOINTS_TTL_MSnow defaults to 0 on Linux and macOS, and 15 minutes on Windows. This reduces no-op work for scanning mount points.π¦ Added a couple of new splash backgrounds because why not
π¦ Reduced
syncGC load and memory consumption by more than 2x by refactoring several performance hotspots including caching, filesystem iteration, mutexes, and bounded concurrency.π¦ Docker container license validation is a bit more robust now. Apologies if you needed to re-authenticate: if you see this happen, please report it!
π¦ Hung child processes (like
dfwhen eth0 drops) are proactively cleaned upπ¦ Improved test coverage: several hundred additional test suites were added, especially around tag management, DB transactions under heavy write contention, and concurrency management
π¦ Error events are now written with month (not day) resolution, to prevent the same error reported more than once a month.
π¦ Moved
scanLibraryFirstandscanLibraryLastto library “sync” settings category, and renamed themsyncLibraryFirstandsyncLibraryLast. The previous names were added as aliases, so prior configuration changes will be migrated to the new names. ThesyncLibraryFirstsetting now defaults totrueifcopyFilesIntoLibraryis true, to make sure we know what’s in your library before copying more stuff in there.π¦ Memory and CPU metadata now respect container quotas (like when under Kubernetes). Thanks for the suggestion, Stephonovich!
π¦
streamFlushMilliscan be adjusted as a setting, and will automatically be increased if PhotoStructure detects stderr/stdout synchronization issues due to slow/overwhelmed systems.π¦ Mountpoint watching is more reliable on Ubuntu and Alpine. We now use both a file watcher on
/proc/mountsandfindmnt --pollif available, and parse the content directly, rather than forkingmountand parsing that content.π¦ PhotoStructure’s (extensive!) continuous integration test suite now runs on macOS, Windows, Ubuntu, and Alpine (prior versions didn’t include Alpine)
π¦ Upgraded Docker container to Alpine 3.15/Node 16 LTS and the latest stable, audited release for all third-party code.
π¦ Deleted
logElapsedMssetting to simplify log formatting codeπ¦ New
minDelayBetweenSpawnMssetting allows for adjustment of process load rampsπ¦ New
enableWebSecuritysetting supports disabling CSP and CORS when on localhost. See the setting for details.π¦ New
maxRetriessetting letssyncretry file imports. If you have a flaky network, or if your computer shows up late for work because it had a hard drive, this can help ensure imports are comprehensive. The default is1. Set this to0to disable retries.π¦ New
ignoredFilesystemTypesLinux-only setting lets you tell PhotoStructure not to walk into non-fs mountpoints. The default of["cgroup", "debugfs", "gvfsd-fuse", "none", "sunrpc", "sysfs", "tracefs"]should work for most people. If you need to edit this, please pop into the forum or Discord and tell us!π¦ Cache dirs
rm -rfed when database migrations are applied. This will fix incorrectly-rotated cached preview HEIFs.

