Skip to main content

43 posts tagged with "release"

View All Tags

1.4.0: Edits written to your files, editing many photos at once, search filters

Β· 18 min read
Niaz Faridani-Rad
Maintainer of LibrePhotos

This release makes the edits you make in LibrePhotos reach your files, lets you change the date and place of many photos at once, adds filters to search, and brings album collections and an archive. Updating gets easier too: the work an update leaves now runs from the app. It also fixes a long list of bugs in scans, dates, faces, places and stacks, and a security problem.

Who should update: Everyone. If other people have an account on your LibrePhotos, update right away: this release fixes a security problem.

What's new for you:

  • With Write edits to files on, your tags, captions, locations and rotations now go into your files too, next to ratings, dates and face tags. Write now under Write existing edits to files writes the edits you made before. See What LibrePhotos writes.
  • Select photos and choose Change date and time… or Set location… to fix a camera with a wrong clock, or place a whole trip at once. See Changing the date of many photos.
  • Click Filters on the search results to narrow them by people, camera, date, place, rating or file type. Search also ignores accents now: "Zurich" finds "ZΓΌrich". See Search filters.
  • Group your albums into Collections, and use Archive to take photos off the timeline while they stay in your albums. See Collections and Archive.
  • Admins: a Finish the upgrade card in the Admin Area runs the work an update leaves and shows how far it got. See The Finish the upgrade card.

Before you update: Make a backup as usual. The Windows app and the single container with its built-in database now also copy the database by themselves before every database update. See Automatic backups.

After you update:

  • Nothing to do for most of it. The first start takes longer than usual, because LibrePhotos repairs some data once, for example duplicate Nextcloud photos. Admins see the remaining work on the Finish the upgrade card under Account menu > Admin Area: it removes photo details from old thumbnails and reads shutter speeds again by itself. The first Scan after the update checks every file once, so it takes longer too.
  • Recommended for everyone: rescan your photos once. Go to Account menu > Library, then the arrow next to Scan > Rescan. This reads what LibrePhotos did not read before: dates, places and camera details from XMP sidecars, the date of MKV, WebM and phone videos, and Google Takeout files. It is done when the job in Account menu > My Jobs has finished. If you skip it, those details only show up for photos that change.
  • Windows app, west of Greenwich (for example in the Americas): the rescan is required. Up to 1.3.0, the Windows app stored scan times wrongly there, so a normal Scan missed files you had edited.
  • Optional: rescan faces once if face detection ever failed on your server, or if other apps wrote face tags into your photos. Go to Account menu > Library and click Rescan in the Rescan Faces row. If you skip it, faces in those photos stay undetected.
  • If you use stacks: go to Photos > Organizing, open Stacks and click Detect Photo Stacks once. Bursts are grouped better now, and old stacks stay as they were until you do this.
  • If you write face tags to files: face tags of rotated photos (most phone portraits) were written to the wrong place. Go to Account menu > Settings > Metadata Options and click Write now under Write existing edits to files to write them again.
  • Admins: settings, log file and Docker changes
    New site settings: Missing photos limit, Scan libraries automatically, Delete photos from the trash after (off by default) and a Features card. New environment variables: STUCK_JOB_HOURS, SIDECAR_PORT_<NAME>, NEXTCLOUD_CA_BUNDLE, FEATURE_VIDEO_PREVIEWS and the thumbnail and video preview sizes; remove DEFAULT_FAVORITE_MIN_RATING from your .env, it is no longer read (see Environment variables). The log file is now librephotos.log: point log shippers and fail2ban filters at the new name (details). The single container Compose file moved to deploy/compose/docker-compose.unified.yml, reads librephotos.env and mounts the database at /var/lib/postgresql: back up the dbcursor folder in your data folder first. The generated secret key moves to protected_media/secret.key; copy it back before you go back to 1.3.0 or older, or everyone has to log in again (details). Scripts that use the API: list endpoints page by 100 with one envelope, errors have one shape, and album ids are numbers everywhere.

Upstream:

πŸš€ LibrePhotos: Your Edits, Written to Your Files

With Write edits to files set to sidecar or media file, LibrePhotos now writes your tags (as keywords), captions, locations and rotations back to the photo, like ratings, dates and face tags before. The new Write existing edits to files row in Settings > Metadata Options writes the edits you made before you turned it on, as a job with progress and Cancel (#2287). Edits go into the sidecar that already exists (IMG.jpg.xmp from digiKam or darktable) instead of a second one, deleting a face also removes its region from the file, and when ExifTool could not write an edit, an orange toast says so instead of a green "done". Reading got the same care: the scan imports captions, title, creator, copyright and more keywords from IPTC, EXIF and XMP, takes dates, GPS, camera and lens from sidecars and from the sidecars of RAW and video variants, reads Google Takeout JSON files, and recognises LibrePhotos' own writes, so a bulk write no longer turns the next scan into a full rescan. The info panel shows camera, lens, exposure, megapixels and coordinates, owners can open an All metadata view with every tag of the file and its sidecars, and zip downloads include the XMP sidecars. With writing Off (the default), LibrePhotos now really leaves your files alone: a scan never writes to them.

πŸš€ LibrePhotos: Edit Many Photos at Once

Select photos and choose Change date and time… to shift them all by the same amount (a camera with a wrong clock, or one left on summer time) or give them one date (#2278), or Set location… to place them all at once, optionally copying the position of a photo taken around the same time (#2279). A photo's location can also be removed now. Changed date rules can be applied to the photos already in the library without a full rescan, captions can be generated for the whole library as one job, the files of selected photos can be rescanned and faces detected for one folder or a selection, and photos can be moved with all their files to another folder of the library. An authenticated scan-files API lets an outside process tell LibrePhotos about new files.

πŸš€ LibrePhotos: Collections, Archive and Groups of People

Albums can be grouped into collections such as Travel or Family, and an album can be in several (#1009). You can upload straight into one of your albums and create an album before it has photos. The new Archive takes photos off the timeline and Memories while they stay in albums, people, places and search (#545), and Photos not in an album shows what is left to sort. Album lists sort by name, count or date, come back to the same place on Back, and events can get a cover you pick. People can be put in groups ("Family", "Work") to filter the People page and the face dashboard by, two people can be merged into one, deleted faces can be restored from a new Deleted tab, and the face dashboard gets a Confirm button on each face and a grid of people with their cover faces.

πŸš€ LibrePhotos: Search Filters

The search results page has a Filters button: people (photos with all of them together, #329), camera, lens, ISO, aperture, focal length, rating, date taken, location and file type, including "Has a RAW file" (#2259). Active filters show as chips you can remove one by one, and they live in the URL, so a link keeps them and Back undoes the last change. You can also search by filters alone, without any words, through Filter photos in the command palette. Search now ignores accents and letter case in both directions: Zurich finds "Zürich", and Lodz finds "Łódź".

πŸš€ LibrePhotos: Safer Updates and Scans

The work an update leaves is no longer only in the release notes: a Finish the upgrade card at the top of the Admin Area runs the follow-up tasks and shows their progress, with a dot on the avatar while anything is left (#2322). While the backend updates its database or starts, the web app shows LibrePhotos is updating instead of empty pages, and a small start-up page answers on the backend port from the first second. The Windows app and the single container with SQLite copy the database before every migration, and an older release refuses to start on a database a newer one has migrated. Scans got safer too: a scan of an unmounted or empty library no longer marks every photo missing, an unreadable folder is skipped instead of failing the whole scan, Cancel really stops a job's work, and a long first scan is no longer failed after 24 hours. Admins can also have LibrePhotos scan every library on a schedule from Site settings, with no host cron job (#235).

πŸš€ LibrePhotos: Security Fix

A logged-in user could change the hash of one of their own photos through the photo API. The hash names the photo's thumbnail files, so rotating the photo afterwards wrote and deleted .webp and .mp4 files outside the thumbnail folders, or replaced another user's thumbnails (GHSA-f7vw-q3m9-4998). The hash is now set by the server only, and every thumbnail path refuses a hash that is not a plain file name. Every API view now also declares who may call it, the token endpoints and failed Basic authentication attempts are rate limited, and the generated secret key is no longer kept in the logs folder. Thanks to shaoyuuuuu for the responsible disclosure.

  • ✨ LibrePhotos: Offline place names: the new ExifTool (offline, built in) Map Provider looks place names up without sending your coordinates anywhere, and it is the default for new installs (existing installs keep their provider). Place names are read by level, so event titles, memories and the location chart name the right town, and Admin Area > Admin Tools > Look up place names again renames every photo after a switch (#2302)
  • ✨ Frontend: The photo page is redesigned as a photo on a mat with a details column and the lightbox actions (favorite, share, download, hide, trash, rotate)
  • ✨ Frontend: The open lightbox photo is in the URL, so Back closes it and a link opens it, and the lightbox asks before it discards an unsaved edit
  • ✨ Frontend: The selection bar only shows while you select, and dangerous one-click actions ask first
  • ✨ LibrePhotos: Trash retention, Empty trash and trashed-on dates on the Trash page
  • ✨ LibrePhotos: Opt-in 1 to 5 star rating mode on the web, read from and written to the file's XMP rating
  • ✨ LibrePhotos: A Documents page, and the photo/video filter on Trash, Recently added, No timestamp and folder pages
  • ✨ LibrePhotos: A view of photos without a location, where Select all selects every one of them (Implemented by err0rgod)
  • ✨ LibrePhotos: Album page menu (rename, sharing, lock, delete), leave an album someone shared with you, person page menu and removing photos from a person
  • ✨ Frontend: Share a public album as a slideshow link (#1457), and public-link photo cards show captions
  • ✨ Frontend: People show as round face crops, and the avatar can be chosen from a person's cover face (Implemented by lalmachado)
  • ✨ LibrePhotos: Event titles are worded in the UI language, and counts, sizes and percentages are full translatable sentences; a new language from Weblate loads without a code change
  • ✨ LibrePhotos: Admins can switch off the animated video previews (#649), set the big thumbnail and video preview sizes, and models for features that are turned off are no longer downloaded
  • ✨ LibrePhotos: Cancel a running upload, drop files anywhere to upload them, and abandoned uploads are cleaned up
  • ✨ Backend: Trust a Nextcloud server with a self-signed or private CA certificate through NEXTCLOUD_CA_BUNDLE
  • ✨ Frontend: Settings and Admin pages get section navigation, clearer explanations of Write edits to files, the default time zone and public sharing, tidier rule tables, an opaque lightbox backdrop and dark-mode maps
  • ✨ Frontend: Accessibility: disabled buttons and menu items say why and stay reachable by keyboard, icon buttons and images have names, and face tiles and job links work from the keyboard
  • ✨ LibrePhotos: The Windows release is smoke-tested before it becomes the latest release, gets a versioned zip name and a SHA256SUMS file
  • ✨ Docs: Step-by-step Windows update, backup, restore and uninstall, an EXIF page true to the code, a text recognition guide, a glossary of the words used in release notes, every environment variable documented, and a note that the new mobile app is not in the stores yet
  • πŸ”¨ LibrePhotos: Dates: a date set by hand always wins over the date rules and can be removed again, MKV, WebM and phone videos get their date, camera and rotation, GPS time zone rules apply on the first scan, upload dates keep the device's local time, and the date picker starts the week on the right day (Implemented by lalmachado)
  • πŸ”¨ Backend: Metadata: sidecars are read under their XMP names, sidecars of RAW and video variants are read, shutter speeds are stored as ExifTool prints them, camera and exposure details are filled, file size comes from the media file and never from a sidecar, and photos store the size they are shown at
  • πŸ”¨ LibrePhotos: Write-back: "Off" is honoured by face tags and every write path, a scan never writes metadata, person names are added to keywords instead of replacing them, rotations go into sidecars as XMP-tiff:Orientation, face regions of rotated, HEIC and AVIF photos land in the right place, and edits made through the metadata API reach the app
  • πŸ”¨ Frontend: Multi-word keywords stay whole in the keyword editor (Implemented by lalmachado)
  • πŸ”¨ Backend: Faces: photos the face service failed on are retried, faces are detected in photos that already have XMP face regions, Cluster Selection Epsilon changes the clusters again (#1996), and faces without a crop no longer cause errors and get their crop back on Scan Faces
  • πŸ”¨ Frontend: Faces dashboard: tab counters match the lists, and the "Unknown - Other" group no longer offers Confirm all, rename or delete
  • πŸ”¨ Backend: Scans: exact copies of a photo are no longer processed again on every scan, a photo whose copy is still on disk is not missing, removing a scan job no longer forces the next scan to read every file (#1104), and 3GP videos with only 3GPP brands are accepted again
  • πŸ”¨ Backend: Places: GPS added, changed or removed in a file reaches the map and Places, a moved photo forgets its old place name when the new one cannot be looked up, and a spot with no place name (open sea) is no longer looked up again and again or shown as a failure
  • πŸ”¨ Backend: Stacks: burst detection reads Canon drive modes, groups per camera by sub-second time, keeps frames without a serial number in their burst, and "highest resolution" never picks a photo of unknown size
  • πŸ”¨ Backend: Nextcloud: repeated scans no longer duplicate photos, and copies follow changes and deletions on the server
  • πŸ”¨ Backend: Jobs: Cancel stops scans, detection, training and embeddings, a cancel survives face clustering progress, stuck-job cleanup counts from a job's last progress (STUCK_JOB_HOURS), and starting a job twice no longer queues a second copy
  • πŸ”¨ LibrePhotos: Users whose scan folders overlap each get their own photo of a shared file, so neither is refused their own photo or sees the other's rotation (#1957)
  • πŸ”¨ Frontend: A list that fails to load shows an error with Retry instead of the empty state, failed saves are reported once, success is only shown after the server answered, and unsaved settings ask before you leave
  • πŸ”¨ Frontend: Select all on "Photos without Timestamps" selects every undated photo, thing albums stay reachable when their covers are hidden, folders with more than 100 subfolders show all of them, the Sharing overview shows and counts your public photos, and valid email addresses are accepted
  • πŸ”¨ Frontend: Colours set with light-dark() no longer vanish in light mode in production builds (Implemented by dotanm)
  • πŸ”¨ Frontend: No stray spinner on page load, one page gutter on every route, the photo grid lays out again when its width changes, and the rotate toast names the direction you chose
  • πŸ”¨ Backend: The seekable copy of a transcoded video starts once the live conversion is far enough ahead (#2002), and zip downloads include the XMP sidecars of a photo's files
  • πŸ”¨ Backend: Windows app: one instance per data folder with tray status from the start, it names the program holding its port instead of quitting silently, sidecar starts are verified, and scan times are stored as real UTC
  • πŸ”¨ Backend: Django is set up off uvicorn's event loop, which fixes a start-up error on some deployments, and backend images share one entrypoint with pinned database and package versions
  • πŸ”¨ Mobile: Face boxes are placed at the right scale, and people keep their covers after a sync
  • πŸ”¨ LibrePhotos: Leftover settings with no effect are gone (LLM-era caption options, the face confidence settings, DEFAULT_FAVORITE_MIN_RATING)
  • πŸ”¨ LibrePhotos: Code health: the backend is type-checked with mypy and split into domain packages, every list endpoint answers in a constant number of queries, every response is declared in the OpenAPI schema (#485), external tools run with timeouts, and dead code is checked in CI
  • πŸ”¨ Frontend: Code health: the web and mobile apps share one API client with one query-key factory, strict TypeScript without a baseline, ESLint as a hard gate, forms on @mantine/form with zod, typed translation keys, and lint rules for lists, dialogs, toasts and styling
  • πŸ”¨ LibrePhotos: Tests and CI: factories for backend test data, real ExifTool round-trip tests for every write path, checks for model and migration drift, formatting, the OpenAPI schema against the zod schemas, the supported Python range, and fixes for tests that timed out under load
  • ✨ LibrePhotos: Renovate dependency updates for the backend, frontend, mobile app and docs, among them ESLint 10.13, TanStack Query 5.104, Playwright 1.64, zod 4.6, i18next 26.4 and pillow-heif 1.8.1, plus better-sqlite3 13 and @types/node 24 for the mobile app
  • πŸ”’ Backend: Every API view declares who may call it, and the token endpoints and failed Basic authentication attempts are rate limited
  • πŸ”’ LibrePhotos: Auth cookies are set in one place with explicit HttpOnly, SameSite and Secure flags
  • πŸ”’ Backend: The generated secret key is kept out of the logs folder, which people zip for bug reports
  • πŸ”’ Backend: The last hand-parsed views read their input through serializers, and ?public=false is no longer treated as public
  • πŸ”’ Backend: A photo's hash can no longer be changed through the photo API, and thumbnail paths always stay inside the media folder (GHSA-f7vw-q3m9-4998)

Full changelog: https://github.com/LibrePhotos/librephotos/compare/1.3.0...1.4.0


If you are interested in how I develop this application, you can watch me develop features live on my channel


Sponsoring development:

My work is sponsored by 1 awesome person!

If you like the work I do, then you can support me via GitHub sponsors or via PayPal

1.3.0: Timeline filter, videos that play everywhere, faster search

Β· 9 min read
Niaz Faridani-Rad
Maintainer of LibrePhotos

This release adds a timeline filter that keeps screenshots and documents out of the way, makes videos play in every browser, makes search, the API and scans a lot faster on less memory, and fixes a set of reported security issues, including two code execution bugs.

Who should update: Everyone. If other people use your LibrePhotos, or you share public links, update right away: this release fixes security problems.

What's new for you:

  • Use the new Filter button on the timeline to hide screenshots and documents, or show only videos or favorites. Save as default keeps your choice.
  • Videos that did not play in your browser before now play, for example iPhone videos in Firefox.
  • If a photo is marked as a screenshot or document by mistake, fix it with the badge in the photo details.
  • Search, scans and the app are faster and need less memory.

After you update:

  • Nothing to do for search and tags. After the update, LibrePhotos reads every photo again in the background so that tags and search use the new model. Search finds more photos while this runs, and the Things albums fill up again. You can follow it under Account menu > My Jobs.
  • Admins: remove photo details from old thumbnails once. Recommended for everyone, required if you use public links. Thumbnails made before 1.3.0 still contain the location and camera details of the photo, and public links show those thumbnails. One cleanup removes that data. Your photos and the thumbnails' pictures stay as they are, and running it twice does no harm.
    • Windows app: open the folder that contains librephotos.exe. Click the address bar of the folder window, type cmd and press Enter. In the black window, paste start /wait librephotos.exe manage strip_thumbnail_metadata and press Enter. If you start LibrePhotos with --data-dir, put the same --data-dir <folder> right after librephotos.exe. It is done when the window shows the prompt again.
    • Docker: in the folder with your docker-compose.yml, run docker compose exec backend python manage.py strip_thumbnail_metadata. Single container: docker exec librephotos python manage.py strip_thumbnail_metadata.
    • More detail: After upgrading from 1.2.1 or older.
  • Kubernetes
    The manifests in deploy/k8s now use 1.3.0 and PostgreSQL 16 instead of 2023w31 and PostgreSQL 13. Read the Kubernetes README before you apply them to an existing install.

Upstream:

πŸš€ LibrePhotos: One Open Model for Tags and Search

Tags, semantic search, and similar photos now all come from OpenCLIP ViT-B/32, trained by LAION on DataComp-XL and released under the MIT licence. It replaces CLIP ViT-B/32 for search and the separate tagging model, so a scan runs one vision model per photo: tagging a photo produces its search embedding in the same pass. The separate clip_embeddings service is gone, and so are the model choices in the site settings, because there is nothing left to choose. Captions (LFM2.5-VL) and faces (InsightFace) are unchanged. See Semantic search.

πŸš€ LibrePhotos: Faster Search, API, and Scans on Less Memory

The Rust and TypeScript backend experiments were much faster than Django, and most of the difference turned out to be queries, app-layer work, and ML memory rather than the language. Every optimization that applies to Django is now in LibrePhotos. On a 50,000 photo library, text search takes about half the time (1.19 s down to 0.56 s), album lists, the date list, user details, site settings, and thumbnails answer faster with fewer queries, and duplicate detection finishes in 27 seconds instead of running for hours. The idle API server uses about half the memory and starts in 3 seconds, heavy libraries load only when they are needed, and a scan with machine learning committed 2.25 GB instead of 10.7 GB in our test.

πŸš€ LibrePhotos: Timeline Filter and Category Correction

The timeline has a Filter button: show photos, videos, or both, hide screenshots, hide documents, or show only favorites, and Save as default to make that your timeline (#2130). Select all acts on exactly what the timeline shows. When the automatic detection gets a photo wrong, fix its category in the lightbox (a badge says whether it was detected or set by you, with Undo) or mark a whole selection as photo, screenshot, or document from the selection menu. The sidebar is tidier too: Memories now appear as a strip on top of the timeline, the organizing pages moved into the Photos menu, and background jobs show as a progress ring around your avatar.

πŸš€ LibrePhotos: Videos That Play in Every Browser

Each video is now probed once during the scan, and its codec, pixel format, and colour transfer are stored. The web app uses that to ask the browser whether it can play the file, and converts only the videos it cannot play (for example HEVC in Firefox), instead of leaving "Always transcode videos" as the only way to play them (#477, #714). HDR videos get an HDR badge on their tiles and correctly tonemapped thumbnails, and a Probe Videos job fills in videos scanned before this release. (Implemented by dotanm)

πŸš€ LibrePhotos: Security Fixes

This release fixes several reported vulnerabilities, so please upgrade. A person name containing a line break could inject options into ExifTool when face tags were written to files, which let a logged-in user run code on the server (GHSA-x465-w9j9-42rp). ExifTool tag names in a user's own date and burst detection rules could do the same in the exif service (GHSA-cgq8-jvp9-r3j7). On the unified image and the Windows standalone build, a crafted /media/ URL could read files outside the media folder (GHSA-cf3f-2fmg-mjj6). Thumbnails kept the original photo's GPS position and camera details, so public links leaked the location even with "Share location information" off (GHSA-j84j-mc98-7wq3). Nextcloud server addresses are now validated on the host the connection really uses, which closes a bypass of the check added in 1.2.0 (GHSA-xq77-cj4c-42qr). The advisory for the album and select_all owner scope fix that shipped in 1.2.0 is published too (GHSA-phvg-g65q-rhq3). Thanks to wk-cs, Seven11Eleven, and dotanm for the responsible disclosure.

  • ✨ Frontend: Lock a user album to protect which photos are in it: locked albums cannot gain or lose photos until you unlock them (#867) (Implemented by krisnaparahita)
  • ✨ LibrePhotos: User settings show where web uploads are stored, and an admin can set a user's upload folder (#2033) (Implemented by dchaudhari7177)
  • ✨ LibrePhotos: Star ratings set in the mobile app are saved on the server and written to the file
  • ✨ Frontend: The lightbox shows the photo's category as a badge on the file row
  • ✨ Frontend: React 19, Mantine 9, zod 4, Vite 8, and TypeScript 6, with strict types across the web app
  • ✨ LibrePhotos: Node.js 24 everywhere, and many Renovate dependency updates
  • ✨ Docs: The README shows real screenshots of the app
  • πŸ”¨ LibrePhotos: Owners can open their own hidden and trashed photos in the lightbox again, and hidden and trashed photos are left out of user, public, place, and folder albums
  • πŸ”¨ LibrePhotos: Deleting a user turns off their public album links, photo links, and public photos (also for users deleted earlier), and deleting a second user no longer fails
  • πŸ”¨ LibrePhotos: Sidecar and other non-media files (RawTherapee .pp3, .aae, Takeout .json) no longer mark a scan as failed, users without a scan folder no longer stop the scan for everyone after them, and --scan-files assigns a file only to the user whose scan folder really contains it
  • πŸ”¨ LibrePhotos: Scan follow-up jobs no longer overwrite photo edits made at the same time (Implemented by dotanm)
  • πŸ”¨ LibrePhotos: Logout works from any page, even when the server refuses the request (Implemented by dotanm)
  • πŸ”¨ Backend: Numeric photo keywords such as years are kept instead of breaking the keyword import (#2152) (Implemented by Boulea7)
  • πŸ”¨ LibrePhotos: Video thumbnails survive a failed rebuild, MKV and WebM videos get a duration, and a person with only video faces no longer empties the People page
  • πŸ”¨ LibrePhotos: Zip downloads stream to disk and work on the unified image, the Windows build, and native installs; uploads are no longer read into memory twice
  • πŸ”¨ Frontend: Day headers show the photo's own date instead of the viewer's time zone, the info panel no longer saves a date edit to the previous photo, Nextcloud settings can be typed into again, renaming an album to an existing name no longer crashes, and grids refresh after favorite, hide, or trash
  • πŸ”¨ Frontend: Photo grid preferences save for users with an avatar, and settings pages show server errors instead of swallowing them
  • πŸ”¨ LibrePhotos: Event titles no longer read "Unknown - Other", and several pages that returned errors (missing albums, expired public links, rotating a photo with a duplicate) are fixed
  • πŸ”’ LibrePhotos: Person names are written to files on a single line, and ExifTool refuses any value with a line break
  • πŸ”’ Backend: ExifTool tag names in date and burst detection rules are validated when they are saved and again in the exif service
  • πŸ”’ LibrePhotos: Direct-mode media serving refuses paths with .. or backslashes and checks that every file stays inside the media folder
  • πŸ”’ LibrePhotos: Thumbnails keep only the colour profile, with no EXIF, GPS, or XMP, and strip_thumbnail_metadata cleans the existing ones
  • πŸ”’ LibrePhotos: Nextcloud addresses are checked on the host the connection actually uses
  • πŸ”’ Backend: The admin delete-user endpoint only deletes: it no longer lists user records or changes scan folders without validation

Full changelog: https://github.com/LibrePhotos/librephotos/compare/1.2.1...1.3.0


If you are interested in how I develop this application, you can watch me develop features live on my channel


Sponsoring development:

My work is sponsored by 1 awesome person!

If you like the work I do, then you can support me via GitHub sponsors or via PayPal

1.2.1: SQLite databases repaired

Β· 3 min read
Niaz Faridani-Rad
Maintainer of LibrePhotos

This point release repairs SQLite databases. SQLite is the database of the unified Docker image and of the standalone Windows build, so if you run either of them, please upgrade. PostgreSQL installs are not affected by the repair.

Before you upgrade (SQLite only): two one-time migrations rebuild some tables. They copy each table before dropping the old one, so make sure you have free disk space of roughly the size of your database. On a test library with 1.6 million album entries and 160,000 faces they took about 30 seconds, and the database file grew by about 650 MB; SQLite reuses that space for later writes, and VACUUM hands it back to the file system. If your library already had photos when you upgraded past the switch to UUID photo IDs in early 2026, run "Rescan All Photos" (Settings, Library) after upgrading so camera, lens, and image size details are read from your files again (see below).

Upstream:

πŸš€ LibrePhotos: SQLite Databases Repaired

When photos moved from content hashes to UUID primary keys in early 2026, the SQLite version of that migration stored every existing photo's new ID with dashes, while Django stores and looks up UUIDs on SQLite without them. Those photos kept showing up in lists, but anything that looks a photo up by its ID missed them: their faces, thumbnails, captions, and metadata were not found, adding one to a new album left the album empty, and tagging or sharing one failed with "FOREIGN KEY constraint failed". The same migration also rebuilt twelve tables (album contents, faces, thumbnails, captions, search, files, and sharing) without their foreign keys and other constraints, on every SQLite install, fresh ones included. This release rewrites the stored IDs into the form Django reads, removes entries that pointed at photos, albums, or people that no longer exist, and rebuilds those tables with their foreign keys, so SQLite now enforces what PostgreSQL always did (#2121).

On the affected installs, the migration that moved camera, lens, and image size details into their own table also skipped the old photos, and the old columns were removed after it. "Rescan All Photos" reads those details from your files again.

  • πŸ”¨ LibrePhotos: The standalone Windows build pins lazy_loader 0.5, which Nuitka needs to compile scikit-image; 0.6 broke the build (#2120)
  • πŸ”¨ Docs: The landing page links the standalone Windows download (#2119)

Full changelog: https://github.com/LibrePhotos/librephotos/compare/1.2.0...1.2.1


If you are interested in how I develop this application, you can watch me develop features live on my channel


Sponsoring development:

My work is sponsored by 1 awesome person!

If you like the work I do, then you can support me via GitHub sponsors or via PayPal

1.2.0: A Windows app, Memories, and faster scans

Β· 10 min read
Niaz Faridani-Rad
Maintainer of LibrePhotos

This release brings a standalone Windows build that needs no Docker, moves every machine learning model to ONNX Runtime and drops PyTorch, brings Memories, makes scanning and the web app a lot faster, and closes a long list of security issues: all multi-user instances should upgrade. Here is an overview of all the new features, improvements, and bug fixes since the last release.

Before you upgrade: the first 100 database migrations are now combined into one. Installs on 2026w10 or later (including every 1.x release) upgrade directly as usual. Installs on an older release must first run 1.1.0 once so its migrations complete, then upgrade to this release. If you skip that step, the migration stops with a hint that names the upgrade path. See Upgrading from a release older than 2026w10.

Upstream:

πŸš€ LibrePhotos: Standalone Windows Build, No Docker Needed

LibrePhotos now runs on a plain Windows PC: download librephotos-windows-x64.zip from the assets of this release, unzip it anywhere, and double-click librephotos.exe. That one program is the web server, the web app, the background workers, and every machine learning service, with ExifTool and ffmpeg bundled next to it. There is nothing else to install: no Python, no Docker, no database server. The database is SQLite in %LOCALAPPDATA%\LibrePhotos, your photos stay where they are, and LibrePhotos lives in the notification area with an icon to open it, open the data or log folder, and quit (which stops everything it started). Your browser opens on the first-time setup, and the models download on the first scan. It is meant for one person's library on their own PC; for a shared server or PostgreSQL, keep using one of the Docker deployments. See the Windows standalone guide for options such as --data-dir and --photos.

πŸš€ LibrePhotos: Every ML Model on ONNX Runtime, PyTorch Removed

Tagging, captioning, semantic search, and face recognition now all run on ONNX Runtime. PyTorch, sentence-transformers, transformers, and llama.cpp are gone from the images, which makes them much smaller and faster to build. Default tagging now uses MobileCLIP-S2 zero-shot over the shared tag vocabulary instead of Places365, semantic search keeps the same CLIP ViT-B/32 weights in ONNX form, and captioning is a single small vision-language model (LFM2.5-VL-450M) that is always available and is prompted with the people and places in the photo, so captions can name who is in the picture and where it was taken. This replaces the BLIP captioner (broken for about a year), im2txt, Moondream, and the optional Mistral caption polish. The GPU image now actually runs the models on the GPU, ML services only start when their feature flag is on, and idle services unload their models to free memory.

πŸš€ LibrePhotos: Memories

A new Memories page shows the photos you took on this day in earlier years (#844). There is one tile per year with a cover, the place, and how many photos it holds; clicking a tile plays that year as a slideshow, "Play all" plays every year in a row, and a gallery switch shows the whole day across the years as one grid. Memories cover the anniversary plus or minus three days, roll over at your own midnight, and scale with the number of years in your library, not the number of photos. Nothing is injected into the timeline and nothing autoplays: if you do not want memories, you simply never open the page. (Implemented by dotanm)

πŸš€ LibrePhotos: Faster Scans and a Faster Web App

The scan pipeline was profiled end to end. The metadata service now reads every tag of a request in one ExifTool command instead of one per tag (about 390 ms down to about 30 ms per photo), thumbnails are decoded once per photo instead of three times, RAW files use the camera's embedded preview when it is good enough, every file is hashed once, face detection keeps the embeddings it already computed, and backend services are reached on 127.0.0.1 instead of localhost, which cost two seconds per request on some systems. In the browser, route code splitting now actually splits routes and translations load on demand, so the main JavaScript chunk shrinks from 3.68 MB to 829 KB. The photo grid moved to TanStack Virtual and drag and drop to dnd-kit.

πŸš€ LibrePhotos: Security Hardening

A thorough authorization review of the backend closed a series of cross-user leaks. User albums and the select_all download could be used to place another user's private photos in your own album and share them again; albums can now only vouch for their owner's photos. Three reported issues are fixed and published: a photo hash existence oracle (GHSA-hq2w-x39h-8wmp), the map API key being returned to anonymous callers (GHSA-6367-m327-c4pf), and other users' job records in /api/rqavailable/ (GHSA-975v-mx44-9jxq). On top of that: day albums, read-only album shares, folder paths, photo metadata lookups, chunked uploads, zip downloads, and hash-keyed photo lookups are scoped to the requesting user; public albums honour their location and time sharing settings; and password reset links can no longer be poisoned through the Host header. Thanks to Hama1cco for the responsible disclosure.

  • ✨ LibrePhotos: Revocable per-photo share links: the lightbox share button now creates a link for a single photo that you can copy, replace, or revoke from the Public Links page (#2028) (Implemented by jonesfionn101-dotcom)
  • ✨ Frontend: Add a face the scan missed by drawing a box on the photo and naming the person in the usual Label faces dialog (Implemented by dotanm)
  • ✨ Frontend: Tag a whole selection at once: select photos anywhere in the grid and press t (Implemented by dotanm)
  • ✨ LibrePhotos: Transcoded videos are now seekable (the converted file is cached after the first play), and the arrow keys seek in the lightbox (#1983) (Implemented by dotanm)
  • ✨ Frontend: Copy a photo to the clipboard from the lightbox with the Copy button or Ctrl/Cmd+C
  • ✨ Frontend: A floating upload progress card shows every file of an upload and its state
  • ✨ LibrePhotos: Optionally give each new user their own data folder (AUTO_CREATE_USER_DIRECTORY, off by default) (#2038) (Implemented by dchaudhari7177)
  • ✨ Backend: Photo descriptions are imported from XMP (dc:description) (Implemented by jdmasa)
  • ✨ LibrePhotos: Native install on every platform: the backend installs with a plain pip install everywhere, with libvips, LibRaw, ExifTool, and ffmpeg all coming from wheels, and the base Docker images shrink accordingly
  • ✨ LibrePhotos: The official mobile app is rebuilt from scratch with Expo and an offline-first sync engine, backed by new delta-sync endpoints on the server; it replaces the old React Native app in the repository
  • ✨ Frontend: Library counter tiles are abbreviated on mobile, with the exact value in a tooltip (#973) (Implemented by krisnaparahita)
  • ✨ Backend: Per-module log levels with LOG_LEVELS=package=LEVEL,...
  • πŸ”¨ LibrePhotos: HDR videos from phones are tonemapped instead of playing washed out
  • πŸ”¨ LibrePhotos: The live video transcode is bounded and no longer hangs on a full pipe (#1920) (Implemented by dotanm)
  • πŸ”¨ LibrePhotos: Faces: hand-drawn and XMP face regions no longer receive a stranger's embedding, face counts count inferred faces correctly, unnamed and deleted faces are shown truthfully, the face dashboard no longer requests pages that moved faces removed, and antelopev2/buffalo_m model zips unpack correctly (Implemented by dotanm)
  • πŸ”¨ LibrePhotos: Scanning: files replaced in place are re-indexed, per-file scan failures are recorded on the job, XMP sidecars stay with their photo, symlink loops are handled, job progress is race-free, and failed photos no longer starve the CLIP embedding job
  • πŸ”¨ LibrePhotos: Folders and scan directories: a folder no longer absorbs a sibling whose name it prefixes, a directory that only shares the allowed root's prefix is refused, a scan directory another user already scans is rejected, and scan directories are validated on user creation (Implemented by dchaudhari7177)
  • πŸ”¨ LibrePhotos: Photos marked public are served to anonymous visitors again, and a MEDIA_FILE rotation is no longer applied twice on rebuild (Implemented by dchaudhari7177)
  • πŸ”¨ LibrePhotos: Orphaned thumbnail files are removed (Implemented by stepheng223)
  • πŸ”¨ LibrePhotos: Database: the album photo indexes and unique constraints lost in migration 0099 are restored, and tag photo counts stay correct when photos stop counting (Implemented by dotanm)
  • πŸ”¨ LibrePhotos: Revoking a public album link and sharing again now mints a new link
  • πŸ”¨ LibrePhotos: The people and user album lists no longer run one query per album, and place albums show before the map reports its bounds
  • πŸ”¨ Frontend: Password managers can remember login credentials again (#924), video tiles no longer download whole clips as images, album covers no longer autoplay every video, and the timeline stays populated after an upload
  • πŸ”¨ Frontend: Single-flight token refresh: concurrent requests share one refresh and an expired session logs out once, plus a series of accessibility and translation fixes
  • πŸ”¨ LibrePhotos: Captioning reports the service's real error, uses the q4 model export that works on all CPUs (including ARM64), and starts the model download when a caption is requested too early
  • πŸ”¨ LibrePhotos: The Windows job cluster works again with django-q2 1.11.1, ML services honour BASE_DATA, and a busy ML service is no longer killed by the watchdog mid-request
  • πŸ”’ LibrePhotos: Photo edits and deletes through the photo API are owner-only, staff included: administrators can no longer edit or delete other users' photos
  • πŸ”’ LibrePhotos: Nextcloud server addresses are validated on every use (loopback, link-local, and reserved addresses are refused) and the download is hardened; set NEXTCLOUD_ALLOW_PRIVATE_ADDRESSES=false to restrict users to Nextcloud servers on the public internet
  • πŸ”’ LibrePhotos: The cover-face and incomplete-faces queries are scoped to the requesting user (Implemented by dchaudhari7177)
  • πŸ”’ LibrePhotos: On the unified and standalone images, password reset mail is only sent when the address is trusted: set FRONTEND_BASE_URL (or list your origin in CSRF_TRUSTED_ORIGINS) to keep self-service password reset working
  • πŸ”’ LibrePhotos: A security policy for private vulnerability reporting (Implemented by dchaudhari7177)
  • ✨ LibrePhotos: Hardened release pipelines (Implemented by Totara-thib), release-pinned base images, a backend refactor of every function with a CRAP score above 30 (tests first), a Playwright end-to-end suite, a frontend type-check ratchet, Docusaurus 3 for the documentation site with a redesigned landing page, and many Renovate dependency updates

If you are interested in how I develop this application, you can watch me develop features live on my channel


Sponsoring development:

My work is sponsored by 1 awesome person!

If you like the work I do, then you can support me via GitHub sponsors or via PayPal

1.1.0: Single sign-on, text search in your photos, and a security fix

Β· 7 min read
Niaz Faridani-Rad
Maintainer of LibrePhotos

This release brings single sign-on, OCR text search with live text, and an important security fix: all multi-user instances should upgrade. Here is an overview of all the new features, improvements, and bug fixes since the last release.

Upstream:

πŸš€ LibrePhotos: OpenID Connect Single Sign-On

You can now log in through any OIDC identity provider (Keycloak, Authentik, Authelia, Zitadel, Google, Entra ID, and friends) via django-allauth. SSO logins mint the same JWT pair as password logins, so everything downstream behaves identically, and password login stays untouched. It is configured through an admin-editable SocialApp plus OIDC_ENABLED, OIDC_BUTTON_LABEL, and OIDC_ALLOW_SIGNUP (off by default) site settings, and ships with setup docs including the exact callback URL to register. The implementation is defensive by design: account linking requires a provider-verified email, ambiguous or blank email matches are refused, and SSO-provisioned accounts are never staff or superuser. (Implemented by dotanm)

πŸš€ LibrePhotos: Screenshot & Document Categories with OCR Text Search and Live Text

Screenshots and documents are now their own media categories with filters, search captions, stats counts, and a dedicated /screenshots page. Screenshots are detected deterministically (localized filename prefixes, Screenshots directories, a conservative PNG-without-camera-metadata fallback), documents via rule-based signals, and manual overrides always win. On top of that, an optional PP-OCRv6-based OCR sidecar extracts the text from your photos: full-text search finds photos by the text in them, and the photo viewer overlays selectable "live text" on the image so you can copy it right off a screenshot. OCR is off by default, so nothing changes until an admin picks an OCR model and runs the extraction job. A fast DB-only backfill classifies existing libraries.

πŸš€ LibrePhotos: Security Fix for select_all Bulk Endpoints (CVE-2026-57943)

Residual fixes for CVE-2026-57943 (#1980, #1981, #1982): the select_all branches of the bulk photo-edit endpoints (make public/private, hide/unhide, trash/restore, favorite, share, delete) built their write target from the shared read-path queryset builder, which drops the owner filter for public: true queries. Any authenticated user could therefore bulk-modify other users' public photos, for example flipping every public photo on the instance to private, hiding them, or moving them to trash. Private photos were never exposed. Every branch now binds the write to the requesting user, with cross-user regression tests for each endpoint.

  • ✨ LibrePhotos: User-defined tags as a first-class entity: create your own tags and organize photos with them, independent of the ML-generated things
  • ✨ LibrePhotos: Self-service password reset, rate-limited and enumeration-safe, closing one of the oldest open requests (#596), powered by admin-configurable outgoing email with SMTP and provider presets (Implemented by dotanm)
  • ✨ LibrePhotos: Deploy-time feature flags: FEATURE_* environment variables let admins disable individual features per deployment, and the Nextcloud integration is now behind an admin toggle
  • ✨ LibrePhotos: Kubernetes-ready liveness/readiness health endpoints plus example k8s manifests
  • ✨ LibrePhotos: Configurable map tile provider: choose PhotoPrism, OpenStreetMap, or no map at all (Implemented by dotanm)
  • ✨ LibrePhotos: Regular users can now see their own background jobs; the worker status view is no longer admin-only (Implemented by dotanm)
  • ✨ LibrePhotos: Scan Phase 1 prefetches known file paths instead of issuing a query per file, making large rescans significantly faster (Implemented by dotanm)
  • ✨ Frontend: Media-type filter: All / Photos / Videos toggle on search, user, place, thing, and person albums (Implemented by dotanm)
  • ✨ Frontend: Server-side Select All: Add-to-album and Create-album now work on a whole query result without loading every photo into the browser (Implemented by dotanm)
  • ✨ Frontend: Faces page improvements: recently tagged people are offered first in the tagging popup, and person groups can be folded and unfolded
  • ✨ Backend: A backend test suite now runs in CI on every pull request
  • πŸ”¨ LibrePhotos: Worked through the open bug backlog, fixing 13 long-standing issues with a regression test for each: Nextcloud scanning/browsing/subfolders (#1066, #1030, #631), auto album generation stuck at 0% (#836), duplicated auto albums (#462), timeouts when tagging many faces (#897), slow album loading (#619), black video playback (#477), and more
  • πŸ”¨ LibrePhotos: Repaired the bugs surfaced by the documentation audit and reconciled the documentation with the code
  • πŸ”¨ LibrePhotos: Photo times are shown in the viewer's timezone, and CPU limits no longer kill the backend
  • πŸ”¨ LibrePhotos: Date edits are written as XMP:DateCreated (the previous tag name was invalid and silently did nothing), and metadata edit history ordering is now deterministic
  • πŸ”¨ LibrePhotos: Thumbnail fixes: UUID-addressed thumbnails are served correctly in direct mode and for thumbnails_big, thumbnail dimensions are read locally so photos can no longer go invisible, and UUID person cover photos are accepted (Implemented by v-marinkov and dotanm)
  • πŸ”¨ LibrePhotos: Faces & XMP: XMP face import is complete, named XMP faces are reconciled with existing people, person albums stay in sync when untagging faces, and person album photo lists get the right album ID (Implemented by v-marinkov)
  • πŸ”¨ LibrePhotos: Event album loading, lightbox, and add-to-album restored (Implemented by v-marinkov)
  • πŸ”¨ LibrePhotos: Media for album-shared photos is served instead of returning a 500 (Implemented by dotanm)
  • πŸ”¨ LibrePhotos: Photos with equal timestamps are ordered by filename (Implemented by terminalchai)
  • πŸ”¨ Frontend: The video player now says why a video will not play instead of guessing (Implemented by dotanm)
  • πŸ”¨ LibrePhotos: Stuck-job cleanup is scheduled automatically, so aged queue blockers no longer grey out the scan buttons forever (Implemented by Shaurya2k06)
  • πŸ”¨ Frontend: The worker-indicator popover is click-toggled so Cancel is reachable (Implemented by dotanm)
  • πŸ”¨ LibrePhotos: Deploy and admin fixes: the unified image no longer ships a stale production.py, the constance admin no longer 500s on FACE_RECOGNITION_MODEL, and backend logging is configurable and single-sourced (#170)
  • πŸ”¨ LibrePhotos: Live Photo companions are returned with their on-disk spelling, and stack review ordering is now deterministic
  • πŸ”’ LibrePhotos: Security: ML model downloads are pinned to known hashes and third-party models are served from the LibrePhotos mirror, so a tampered or partial download can no longer land in the model directory
  • ✨ LibrePhotos: Python 3.14, roughly 30 Renovate dependency updates across backend and frontend, and updated documentation: frontend contribution page rewritten and dev-server commands corrected

If you are interested in how I develop this application, you can watch me develop features live on my channel


Sponsoring development:

My work is sponsored by 1 awesome person!

If you like the work I do, then you can support me via GitHub sponsors or via PayPal

1.0.0 to 1.0.3: Switching to version numbers

Β· 3 min read
Niaz Faridani-Rad
Maintainer of LibrePhotos

LibrePhotos switched to semantic versioning: the release after 2026w25 is 1.0.0, and releases are numbered from now on instead of being named after the week. 1.0.1, 1.0.2 and 1.0.3 followed within two days with small fixes. This post collects all four.

Upstream:

πŸš€ LibrePhotos: Switching to Semantic Versioning

1.0.0 is the first numbered release. Next to the new version scheme, it carries the fixes merged since 2026w25:

  • πŸ”¨ Backend: Clear the old 128-dim face encodings in migration 0126 with a set-based UPDATE (#1872) (Implemented by dotanm)
  • πŸ”¨ Backend: Harden metadata reading against transient exif-service failures (#1873) (Implemented by dotanm)
  • πŸ”¨ Backend: Retry transient face-service connection drops (#1874) (Implemented by dotanm)
  • πŸ”¨ Backend: Keep the latest finished job per type when old jobs are cleaned up, so the scan keeps its baseline (#1875) (Implemented by dotanm)
  • πŸ”¨ Backend: Numeric EXIF tag values are read as text in the date-time extractor (#1879) (Implemented by dotanm)
  • ✨ Backend: PyExifTool is pinned to 0.4.9 (#1882), and the backend dependencies are updated (#1877)
  • πŸ”’ Frontend: Security updates for @babel/runtime 7.26.10 (#1820) and lodash 4.18.1 (#1821)

1.0.1 and 1.0.2:

  • πŸ”¨ LibrePhotos: Fix the GPU backend image (image-backend-gpu)

1.0.3:

  • πŸ”¨ Frontend: Non-admin users no longer see a flood of "Required at results.N.…" popups on the main page (#1888, #1889). The user list is now validated leniently, and the non-sensitive public_sharing flag is back in the public user serializer so the public-user discovery page keeps working. Sensitive fields (email, scan directory, Nextcloud credentials, superuser flag) remain hidden.

Full changelog: https://github.com/LibrePhotos/librephotos/compare/2026w25...1.0.3


If you are interested in how I develop this application, you can watch me develop features live on my channel


Sponsoring development:

My work is sponsored by 1 awesome person!

If you like the work I do, then you can support me via GitHub sponsors or via PayPal

Development: April - June 2026

Β· 4 min read
Niaz Faridani-Rad
Maintainer of LibrePhotos

This is a big milestone release β€” LibrePhotos has moved to a monorepo! Here is an overview of all the new features, improvements, and bug fixes since the last release.

Upstream:

πŸš€ Monorepo: One Repository for Everything

The backend, frontend, mobile app, documentation, and Docker deployment now all live in a single repository at LibrePhotos/librephotos, with the full git history of each project preserved. The code is organised under apps/backend, apps/frontend, apps/mobile, apps/docs, and deploy/. This comes with a unified set of GitHub Actions workflows, Dockerfiles and compose files rewritten for the new build context, a single Renovate configuration, and a fresh root README and contribution guide. One place to file issues, one place to open pull requests.

πŸš€ LibrePhotos: New Face Recognition Engine (InsightFace / ArcFace)

Face recognition has been migrated from dlib to an InsightFace / ArcFace stack running on ONNX Runtime. The new engine produces 512-dimensional ArcFace embeddings (up from the old 128-dim dlib encodings) for more accurate face matching, ships onnxruntime-gpu in the backend-gpu image for GPU-capable setups, and lets you choose the face recognition model from Site Settings (buffalo_sc by default). A migration clears the old 128-dim encodings and stale clusters — just re-run a face scan to regenerate them with the new model. The rollout also includes a batch of robustness fixes: RGBA→RGB conversion before recognition, proper JSON error responses from the face service, and several clustering and encoding fixes.

πŸš€ Frontend: Subdirectory Deployment Support

LibrePhotos can now be hosted under a subpath (e.g. https://example.com/photos/) thanks to new PUBLIC_URL support in the frontend build. (Implemented by dashitongzhi)

  • ✨ LibrePhotos: The search API can now filter results by media type, returning only photos or only videos (Implemented by dotanm)
  • ✨ LibrePhotos: Re-adopt files that reappear after being flagged as missing (Implemented by dotanm)
  • ✨ LibrePhotos: Bump geocode timeout to 10s and rate-limit reverse geocoding per provider (Implemented by dotanm)
  • ✨ LibrePhotos: Add timeouts to sidecar HTTP calls so background jobs can't wedge forever (Implemented by dotanm)
  • ✨ LibrePhotos: Only mark a job as failed once its error rate crosses a threshold, so transient errors no longer fail the whole job (Implemented by dotanm)
  • ✨ LibrePhotos: Collapse the AlbumThing photo-count refresh into a single sweep for faster missing-photo deletion (Implemented by dotanm)
  • ✨ LibrePhotos: Finish migrating the map provider off the retired Photon service
  • ✨ Frontend: Select All now works for the Download action (Implemented by dotanm)
  • ✨ Frontend: Clearer scan feedback β€” the scan button is guarded when no scan directory is set, plus a worker activity indicator (Implemented by dotanm)
  • ✨ Backend: Python 3.14, Django 5.x, ExifTool 13.57 and dependency updates; ruff formatting adopted across the backend
  • πŸ”¨ LibrePhotos: Keep emptying the trash when a single photo fails to delete (#873) (Implemented by dotanm)
  • πŸ”¨ LibrePhotos: Keep zero-valued GPS, rating and burst-index values in metadata instead of dropping them as falsy (Implemented by dotanm)
  • πŸ”¨ LibrePhotos: Fix median_value crash on even counts and off-by-one on odd counts (Implemented by dotanm)
  • πŸ”¨ LibrePhotos: Guard album-date geolocation against missing or short place data (#1268) (Implemented by dotanm)
  • πŸ”¨ LibrePhotos: Run delete-missing-photos asynchronously and fix its correctness (#1405) (Implemented by dotanm)
  • πŸ”¨ LibrePhotos: Deduplicate AlbumThing cover photos (Implemented by dotanm)
  • πŸ”¨ LibrePhotos: Pad short returns from metadata extraction (Implemented by dotanm)
  • πŸ”¨ Docs: Document non-destructive photo rotation in the lightbox
  • πŸ”’ LibrePhotos: Security β€” fix SSRF in Nextcloud server address validation (CWE-918) (#1863) (Implemented by alanturing881)
  • πŸ”’ LibrePhotos: Security β€” pytest updated to v9.0.3 and a Django security update applied
  • ✨ LibrePhotos: Updated dependencies and updated language strings from the community

If you are interested in how I develop this application, you can watch me develop features live on my channel


Sponsoring development:

My work is sponsored by 1 awesome person!

If you like the work I do, then you can support me via GitHub sponsors or via PayPal

Development: March - April 2026

Β· 3 min read
Niaz Faridani-Rad
Maintainer of LibrePhotos

Here is an overview of all the new features, improvements, and bug fixes since the last release.

Upstream:

πŸš€ LibrePhotos: Face Tag Writeback to Sidecar Files

Face recognition results can now be written back to XMP-MWG-RS sidecar files, making your face tagging data portable and compatible with other photo management tools. A new setting controls this behavior, and the metadata handling has been refactored into a dedicated api/metadata package for cleaner architecture.

πŸš€ LibrePhotos: Server Logs Viewer

A new API endpoint (GET /api/serverlogs/view) allows reading server logs directly from the application, paving the way for in-app log inspection without shell access.

πŸš€ Frontend: Services Health Monitoring Panel

The admin page now features a dedicated services health monitoring panel showing the real-time status of all background services. Start and Stop controls are shown contextually β€” only when the action makes sense based on the current service health.

πŸš€ Frontend: Server Logs Card

A new server logs card in the admin panel lets you inspect server logs directly from the UI, powered by the new /api/serverlogs/view endpoint.

  • ✨ LibrePhotos: Optimize N+1 queries in photo serializers for better API performance
  • ✨ LibrePhotos: ConcurrentRotatingFileHandler adopted for multi-process-safe log rotation
  • ✨ LibrePhotos: Log rotation default increased to 200MB with configurable limits via CONSTANCE settings
  • ✨ Frontend: New and updated translations: Danish, German, Korean, Tamil, Dutch, Ukrainian
  • πŸ”¨ LibrePhotos: Fix motion photo video serving β€” use actual file path instead of hardcoded _1.mp4 suffix
  • πŸ”¨ LibrePhotos: Fix duplicate face detection β€” replace margin-based overlap check with IoU (Intersection over Union) for accurate deduplication
  • πŸ”¨ LibrePhotos: Fix photo captioning crash when the tag service returns a non-JSON response
  • πŸ”¨ LibrePhotos: Fix PhotoMetadataSummarySerializer to correctly expose @property fields
  • πŸ”¨ LibrePhotos: Fix UnorderedObjectListWarning in paginated querysets
  • πŸ”¨ LibrePhotos: Fix tagging model configuration errors when TAGGING_MODEL key is absent from CONSTANCE config
  • πŸ”¨ Frontend: Fix login error notification β€” show proper error message on failed login attempt
  • πŸ”¨ Frontend: Fix GPU memory stat parsing to accept both string and number values
  • πŸ”’ LibrePhotos: Security β€” Django updated to v5.2.12
  • πŸ”’ LibrePhotos: Security β€” Fix privilege escalation: prevent non-admin users from creating superuser accounts via the registration API
  • πŸ”’ Frontend: Security β€” lodash updated to v4.18.1
  • ✨ LibrePhotos: Updated dependencies and updated language strings from the community

If you are interested in how I develop this application, you can watch me develop features live on my channel


Sponsoring development:

My work is sponsored by 3 awesome people!

If you like the work I do, then you can support me via GitHub sponsors or via PayPal

Development: 2025 - November to February

Β· 5 min read
Niaz Faridani-Rad
Maintainer of LibrePhotos

This is a big one! A lot has changed since the last release. Here is an overview of all the new features, improvements and bug fixes.

Upstream:

πŸš€ LibrePhotos: Photo Stacks & File Variants

Inspired by PhotoPrism, RAW+JPEG pairs and Live Photos are now treated as a single photo entity with multiple file variants, instead of creating separate entries. The scanner now uses a two-phase architecture: first grouping files by directory and basename, then processing each group together. A new repair job automatically merges any previously ungrouped file variants after each scan. This comes with a completely new UI for viewing and managing stacks, file variants in the lightbox sidebar, RAW badge overlays, and a unified organizing page for stacks and duplicates.

πŸš€ LibrePhotos: Duplicate Detection

A brand-new duplicate detection system using perceptual hashing! It detects exact copies via hash comparison and visual duplicates using a memory-efficient two-pass algorithm that works well even with very large collections. The API supports a batch_size parameter to fine-tune performance for your hardware.

πŸš€ LibrePhotos: Photo UUID Primary Key & Structured Metadata

The Photo model's primary key has been changed from image_hash to UUID, which is a massive internal refactor touching models, APIs, serializers, background tasks, and tests. Alongside this, a new structured PhotoMetadata model with edit history has been added, enabling richer metadata management.

πŸš€ Frontend: Public Album Sharing Options

Public album sharing now supports fine-grained per-album settings. You can control the visibility of location, camera info, timestamps, captions, and faces for each shared album. Default sharing preferences can be configured in user settings, and individual albums can override them.

πŸš€ Frontend: Slideshow Mode

A new slideshow mode has been added to the lightbox with a configurable slide interval. Sit back and enjoy your photos!

πŸš€ Frontend: Explore Page

The navigation dropdown has been replaced with a dedicated explore page, giving you a much better overview of your albums, people, places, things and events at a glance.

πŸš€ Frontend: Redesigned Album & Statistics Pages

The album page and all statistics/data visualization pages (face clusters, social graph, word clouds, timeline, place tree) have been completely redesigned with a fresh new look.

πŸš€ Frontend: Spotlight Search

A new spotlight search powered by Mantine replaces the old search bar, providing a faster and more intuitive search experience with keyboard navigation.

πŸš€ Frontend: Select All

You can now select all photos in a view for bulk operations like deleting, sharing, or adding to albums.

  • ✨ LibrePhotos: Switch from Photon to Nominatim as default reverse geocoding provider
  • ✨ LibrePhotos: New endpoint for getting all albums for a photo
  • ✨ LibrePhotos: Improved scan error handling and stack detection
  • ✨ LibrePhotos: Split directory_watcher into a well-structured package with two-phase scan
  • ✨ LibrePhotos: Social graph error logging improvements
  • ✨ LibrePhotos: ARM CPU instruction set detection refactored and fixed
  • ✨ LibrePhotos: Add SQLite support for UUID primary key migration
  • ✨ Frontend: Fullscreen mode in lightbox
  • ✨ Frontend: Switched to MapLibre GL with PhotoPrism tile server for all map views
  • ✨ Frontend: Improved UX for setting photo location with geocode search
  • ✨ Frontend: Improved UX for albums with cover picker and better album list view
  • ✨ Frontend: New languages added to the UI
  • ✨ Frontend: Improved first time setup experience and default empty state messages
  • ✨ Frontend: File variants UI in photo details sidebar
  • ✨ Frontend: Metadata section component in lightbox
  • ✨ Frontend: Download options now include stacked/variant photos
  • ✨ Frontend: Improved scrolling behavior
  • ✨ Frontend: Extract inline styles into CSS modules (Implemented by sickelap)
  • ✨ Frontend: Replaced airbnb ESLint config with eslint:recommended + prettier; removed unused dependencies (react-spring, react-vis, react-d3-graph)
  • ✨ Frontend: Major dependency updates (jwt-decode v4, i18next v25, react-i18next v15, @tabler/icons-react v3, react-cookie v7, and more)
  • ✨ Frontend: Replaced ReactPlayer with native video element for better video playback
  • ✨ Docker: Remove SQLite data dir creation from entrypoint
  • πŸ”¨ LibrePhotos: Fix scan progress stuck below 100% when files are skipped or invalid (Implemented by Nikoh77)
  • πŸ”¨ LibrePhotos: Fix scan job not being marked as finished when no files need processing (Implemented by Nikoh77)
  • πŸ”¨ LibrePhotos: Fix bugs when not having any data
  • πŸ”¨ LibrePhotos: Fix big thumbnail content type for proxied responses (#1271)
  • πŸ”¨ LibrePhotos: Fix social graph SQL query to work with UUID primary key
  • πŸ”¨ LibrePhotos: Fix UnboundLocalError in face extraction from dlib
  • πŸ”¨ LibrePhotos: Fix pagination crash with negative page_size
  • πŸ”¨ Frontend: Fix video playback issues in lightbox
  • πŸ”¨ Frontend: Fix photo list flickering
  • πŸ”¨ Frontend: Fix server stats parsing for CPU info fields
  • πŸ”¨ Frontend: Fix upload issues with sequential file uploads and query invalidation
  • πŸ”¨ Frontend: Fix number of deleted images when selecting all
  • πŸ”¨ Frontend: Fix face dashboard issues and face mutation hooks
  • πŸ”¨ Frontend: Fix issues with shared by me albums view
  • πŸ”¨ Frontend: Fix memory hog issue
  • πŸ”¨ Frontend: Fix login popup messages
  • ✨ Frontend: Fix ESLint errors (Implemented by sickelap)
  • ✨ LibrePhotos: Updated dependencies and updated language strings from the community

If you are interested on how I develop this application, you can watch me develop features live on my channel


Sponsoring development:

My work is sponsored by 3 awesome people!

If you like the work I do, then you can support me via GitHub sponsors or via PayPal

Development: 2026 - February

Β· One min read
Niaz Faridani-Rad
Maintainer of LibrePhotos

This release introduces a multi-model tagging system, giving you the choice of two different AI models for auto-tagging your photos.

Upstream:

  • πŸš€ LibrePhotos/Frontend: Multi-Model Tagging System
    • You can now choose between two tagging models in Site Settings β†’ Tagging Model:
      • Places365 β€” The classic scene recognition model (default)
      • SigLIP 2 β€” Google's latest vision-language model, running as ONNX. Uses zero-shot classification against a curated vocabulary of 900+ real-world photo tags (people, animals, nature, activities, scenes, weather, and more). Returns the top 10 most relevant tags.
    • Tags are stored per model β€” switching between models does not delete previously generated tags, and does not require rescanning your library.
    • The lightbox sidebar now shows tags with color-coded badges for each model (green for SigLIP 2, blue for Places365).
    • Things Albums are dynamically filtered to only show tags from the currently active tagging model.
    • Search results use tags from the selected model.

If you are interested on how I develop this application, you can watch me develop features live on my channel


Sponsoring development:

My work is sponsored by 3 awesome people!

If you like the work I do, then you can support me via GitHub sponsors or via PayPal