photoprism/internal/entity
Ömer Duran 3ea547a564
Lightbox: Add support for 360° photos and videos #352 #4718 #5623
* Lightbox: Add 360° photo and video sphere viewer support

Wires Photo Sphere Viewer v5 into the lightbox for equirectangular
media. Backend detects UsePanoramaViewer and IsPhotosphere markers
alongside ProjectionType, and exposes Panorama + Projection in the
viewer / search result DTOs so the frontend can dispatch on the
projection type. The renderer (ThreeJS + PSV core + video plugin +
equirectangular video adapter) is lazy-loaded into a dedicated
webpack chunk so the base bundle is unchanged for users with no
360° media. PhotoPrism's existing lightbox controls drive the
underlying HTMLVideoElement that the PSV adapter creates for 360°
videos. Includes Vitest coverage for the sphere helper and the
lightbox dispatch, plus a TestCafe smoke spec and three new
ExifTool metadata fixtures.

* Lightbox: Fix 360° controls leak, hide zoom button, add sidebar icon

* Lightbox: Fix 360° touch panning and navigation arrows

* Lightbox: Fix fast swipe switching photos on 360° slides

Suppress PhotoSwipe swipe/drag navigation at its source via the dispatched pointer hook while a sphere slide is active, since the per-element gesture trap is outrun by a fast swipe whose pointer leaves the sphere container (touch-capable Windows). UI controls stay excluded so buttons and arrows still navigate.

* Lightbox: Route only equirectangular media to the 360° viewer

Routing was based on the loose photo_panorama flag for videos, which is also
true for cubemap and merely-wide (aspect > 1.9) videos, so non-equirectangular
media wrongly opened, distorted, in the sphere viewer. Route strictly on the
equirectangular projection for both photos and videos. Surface the video file's
projection in the viewer DTO via Photo.MediaProjection() (the primary search row
is a poster JPEG with no projection). Add an MP4 metadata-detection test.

* Lightbox: Open tagless 2:1 360° videos in the sphere viewer

Many 360° videos carry no projection tag PhotoPrism can read, so strict
equirectangular-projection routing left them in the flat player. Fall back to
equirectangular's defining 2:1 frame for panorama-flagged videos without a
projection, via is360Equirectangular() in common/sphere.js, while cubemap and
ultrawide (~2.35:1) clips stay flat.

* Tests: Make the 360° sphere video test assert instead of skipping
2026-06-17 03:26:25 +02:00
..
legacy Docs: Bump copyright year to 2026 in Go package headers 2026-05-25 14:39:38 +00:00
migrate Meta: Update go-mp4, regenerate NOTICE, fix XMP doc spelling #2260 #5563 2026-06-17 00:14:33 +00:00
query Metadata: Add Camera Make and Model updates via CLI & API #5663 #5656 2026-06-15 09:25:44 +00:00
schema Cleanup: Fix formatting of Markdown files 2026-02-14 12:10:43 +01:00
search Lightbox: Add support for 360° photos and videos #352 #4718 #5623 2026-06-17 03:26:25 +02:00
sortby Docs: Bump copyright year to 2026 in Go package headers 2026-05-25 14:39:38 +00:00
testdata/album Auth: Add CLI command to create access tokens for apps #782 #808 #3943 2024-01-05 16:31:07 +01:00
album.go Events: Refactor entity change notifications for sharing features #1307 2026-06-11 15:40:57 +00:00
album_cache.go Docs: Improve code comments in internal/entity/album*.go 2025-10-02 15:59:38 +02:00
album_cache_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
album_fixtures.go Tests: Adapt fixtures to have valid slugs 2025-08-19 19:44:23 +02:00
album_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
album_test.go Events: Refactor entity change notifications for sharing features #1307 2026-06-11 15:40:57 +00:00
album_user.go Logs: Add package pkg/log/status to provide generic outcome constants 2025-10-21 14:42:05 +02:00
album_yaml.go Entity: Clip album, photo, and folder paths via shared ClipPath #5615 2026-06-03 09:34:40 +00:00
album_yaml_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
auth_client.go Cluster: Add a per-instance node DisplayName field 2026-06-09 07:23:05 +00:00
auth_client_add.go OAuth2: Remove client soft delete and fix client add command #213 #3943 2024-01-29 21:08:01 +01:00
auth_client_add_test.go Auth: Refactor cluster configuration and provisioning API endpoints #98 2025-09-24 08:28:38 +02:00
auth_client_data.go Cluster: Declare per-node group config via instance env/flags 2026-06-12 18:52:54 +00:00
auth_client_fixtures.go Backend: Move unix package to /pkg/time/unix 2024-07-03 09:31:27 +02:00
auth_client_fixtures_test.go Auth: Refactor cluster configuration and provisioning API endpoints #98 2025-09-24 08:28:38 +02:00
auth_client_test.go Cluster: Add a per-instance node DisplayName field 2026-06-09 07:23:05 +00:00
auth_session.go Cluster: Collect OIDC groups at login for group-based admission 2026-06-12 16:08:38 +00:00
auth_session_cache.go Logs: Add package pkg/log/status to provide generic outcome constants 2025-10-21 14:42:05 +02:00
auth_session_cache_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
auth_session_client.go Account: Generate app password from the UI #808 #4114 2024-04-08 10:44:43 +02:00
auth_session_client_test.go Backend: Move unix package to /pkg/time/unix 2024-07-03 09:31:27 +02:00
auth_session_data.go Cluster: Collect OIDC groups at login for group-based admission 2026-06-12 16:08:38 +00:00
auth_session_data_test.go Cluster: Collect OIDC groups at login for group-based admission 2026-06-12 16:08:38 +00:00
auth_session_delete.go Logs: Add package pkg/log/status to provide generic outcome constants 2025-10-21 14:42:05 +02:00
auth_session_delete_test.go Go: Apply go fix modernizations across backend packages 2026-02-20 03:54:33 +01:00
auth_session_fixtures.go Auth: Gate app passwords behind a settings feature flag #5647 2026-06-09 17:36:24 +00:00
auth_session_fixtures_test.go Auth: Refactor cluster configuration and provisioning API endpoints #98 2025-09-24 08:28:38 +02:00
auth_session_jwt.go Cluster: Improve usage of canonical terms in code/docs based on glossary 2026-02-23 16:16:29 +01:00
auth_session_jwt_test.go Lint: Resolve gosec findings and add embedding dist benchmark 2026-03-03 17:17:49 +01:00
auth_session_login.go Logs: Add package pkg/log/status to provide generic outcome constants 2025-10-21 14:42:05 +02:00
auth_session_login_test.go Lint: Resolve gosec findings and add embedding dist benchmark 2026-03-03 17:17:49 +01:00
auth_session_report.go Auth: Refactor user management API and CLI commands #98 2023-03-08 23:30:39 +01:00
auth_session_report_test.go Tests: Add unit tests 2023-08-09 15:56:16 +02:00
auth_session_test.go Auth: Gate app passwords behind a settings feature flag #5647 2026-06-09 17:36:24 +00:00
auth_tokens.go Security: Add gosec fixes with shared URL and fs validation helpers 2026-03-03 16:38:41 +01:00
auth_tokens_test.go Tests: Add unit tests 2023-08-09 15:56:16 +02:00
auth_user.go Auth: Revoke derived sessions & gate app passwords by login state #5647 2026-06-15 23:41:01 +00:00
auth_user_add.go OIDC: Improve CLI commands and add AuthIssuer to users and sessions #782 2024-07-09 11:01:59 +02:00
auth_user_cli.go Auth: Revoke derived sessions & gate app passwords by login state #5647 2026-06-15 23:41:01 +00:00
auth_user_default.go Logs: Add package pkg/log/status to provide generic outcome constants 2025-10-21 14:42:05 +02:00
auth_user_details.go Go: Apply go fix modernizations across backend packages 2026-02-20 03:54:33 +01:00
auth_user_details_test.go Lint: Resolve gosec findings and add embedding dist benchmark 2026-03-03 17:17:49 +01:00
auth_user_fixtures.go Tests: Add unit tests #4973 2025-05-02 18:39:40 +02:00
auth_user_fixtures_test.go Auth: Refactor cluster configuration and provisioning API endpoints #98 2025-09-24 08:28:38 +02:00
auth_user_legacy.go Auth: Improve legacy user search command and add tests #98 2022-10-14 02:48:23 +02:00
auth_user_legacy_test.go Auth: Improve legacy user search command and add tests #98 2022-10-14 02:48:23 +02:00
auth_user_report.go Auth: Refactor user management API and CLI commands #98 2023-03-08 23:30:39 +01:00
auth_user_report_test.go Tests: Add unit tests 2023-08-11 14:48:28 +02:00
auth_user_settings.go Users: Set the user UID when creating user settings 2026-05-31 00:23:18 +00:00
auth_user_settings_test.go Users: Set the user UID when creating user settings 2026-05-31 00:23:18 +00:00
auth_user_share.go Go: Apply go fix modernizations across backend packages 2026-02-20 03:54:33 +01:00
auth_user_share_fixtures.go Search: Change time_format to RFC3339 and add "edited" filter #4300 2024-05-27 09:16:21 +02:00
auth_user_share_fixtures_test.go Auth: Refactor cluster configuration and provisioning API endpoints #98 2025-09-24 08:28:38 +02:00
auth_user_share_test.go Tests: Use valid fixture IDs so entity tests pass on MariaDB 2026-05-31 00:23:23 +00:00
auth_user_test.go Auth: Revoke derived sessions & gate app passwords by login state #5647 2026-06-15 23:41:01 +00:00
camera.go Events: UID-only camera/lens/country payloads & people refetch #5663 #1307 2026-06-15 11:37:26 +00:00
camera_cache.go Metadata: Add Camera Make and Model updates via CLI & API #5663 #5656 2026-06-15 09:25:44 +00:00
camera_fixtures.go Search: Change time_format to RFC3339 and add "edited" filter #4300 2024-05-27 09:16:21 +02:00
camera_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
camera_makes.go Metadata: Add Camera Make and Model updates via CLI & API #5663 #5656 2026-06-15 09:25:44 +00:00
camera_models.go Metadata: Add Camera Make and Model updates via CLI & API #5663 #5656 2026-06-15 09:25:44 +00:00
camera_test.go Events: UID-only camera/lens/country payloads & people refetch #5663 #1307 2026-06-15 11:37:26 +00:00
camera_types.go Scans: Add scan detection for epson scanner #5703 2025-06-26 11:42:22 +02:00
camera_types_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
category.go Docs: Update code comments in internal/entity/category.go and details.go 2025-10-02 16:19:57 +02:00
category_fixtures.go Backend: Update label photo count and refactor entity fixtures 2020-05-10 19:43:49 +02:00
category_test.go Code clean-up (make fmt) 2019-12-21 17:24:29 +01:00
cell.go Entity: Fix cell lookups 2026-03-07 11:48:20 +01:00
cell_fixtures.go Tests: Refactor unit tests after uid change #4560 2024-10-21 13:16:06 +02:00
cell_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
cell_test.go Entity: Fix cell lookups 2026-03-07 11:48:20 +01:00
clip.go Entity: Rename Sanitize* to Clip* and string.go to clip.go 2026-06-03 09:50:56 +00:00
clip_test.go Entity: Rename Sanitize* to Clip* and string.go to clip.go 2026-06-03 09:50:56 +00:00
country.go Events: UID-only camera/lens/country payloads & people refetch #5663 #1307 2026-06-15 11:37:26 +00:00
country_cache.go Index: Improve save / update functions, remove orphans 2021-02-06 16:30:30 +01:00
country_fixtures.go Backend: Refactor test fixtures 2020-05-11 14:49:53 +02:00
country_test.go Events: UID-only camera/lens/country payloads & people refetch #5663 #1307 2026-06-15 11:37:26 +00:00
db.go Entity: Store created and updated timestamps with second precision 2026-05-31 01:13:27 +00:00
db_conn.go Config: Centralize SQL driver identifiers in pkg/dsn #5588 2026-05-17 14:47:43 +00:00
db_conn_test.go Auth: Open album share links in the regular user interface #98 #782 2022-10-02 11:38:30 +02:00
deprecated.go Routing: Prefix frontend UI routes with /library #840 #2466 2022-10-15 21:54:11 +02:00
details.go Go: Apply go fix modernizations across backend packages 2026-02-20 03:54:33 +01:00
details_fixtures.go UX: Add batch edit dialog and API endpoints #271 #5324 2025-11-19 11:20:34 +01:00
details_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
details_test.go Tests: Use valid fixture IDs so entity tests pass on MariaDB 2026-05-31 00:23:23 +00:00
duplicate.go Auth: Session and ACL enhancements #98 #1746 2022-09-28 09:01:17 +02:00
duplicate_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
entity.go Entity: Rename Sanitize* to Clip* and string.go to clip.go 2026-06-03 09:50:56 +00:00
entity_const.go Assets: Rename example fixtures directory to "samples" 2026-03-06 18:47:33 +01:00
entity_count.go Go: Apply go fix modernizations across backend packages 2026-02-20 03:54:33 +01:00
entity_count_test.go Download: Add Disabled, Originals, MediaRaw & MediaSidecar Flags #2234 2022-04-15 09:42:07 +02:00
entity_counts.go Config: Centralize SQL driver identifiers in pkg/dsn #5588 2026-05-17 14:47:43 +00:00
entity_counts_test.go Entity: Fix nil-DB race in async count/cover updates #5551 2026-05-03 10:49:28 +00:00
entity_errors.go API: Ensure slugs are not empty before saving/creating labels #4761 2025-02-04 03:35:01 +01:00
entity_fixtures.go API: Rename /batch/photos endpoint to /batch/photos/edit #271 2025-05-04 14:09:23 +02:00
entity_init.go Config: Centralize SQL driver identifiers in pkg/dsn #5588 2026-05-17 14:47:43 +00:00
entity_save.go Go: Apply go fix modernizations across backend packages 2026-02-20 03:54:33 +01:00
entity_save_test.go Lint: Resolve gosec findings and add embedding dist benchmark 2026-03-03 17:17:49 +01:00
entity_tables.go Security: Add gosec fixes with shared URL and fs validation helpers 2026-03-03 16:38:41 +01:00
entity_test.go TestMain: Handle "defer" before os.Exit #5330 2025-11-21 18:58:36 +01:00
entity_time.go Search: Change time_format to RFC3339 and add "edited" filter #4300 2024-05-27 09:16:21 +02:00
entity_time_test.go Entity: Store created and updated timestamps with second precision 2026-05-31 01:13:27 +00:00
entity_update.go Go: Apply go fix modernizations across backend packages 2026-02-20 03:54:33 +01:00
entity_update_test.go Entity: Store created and updated timestamps with second precision 2026-05-31 01:13:27 +00:00
entity_values.go Go: Apply go fix modernizations across backend packages 2026-02-20 03:54:33 +01:00
entity_values_test.go CLI: Add "photoprism vision reset" command to reset metadata #5233 2025-09-29 18:59:52 +02:00
error.go Backend: Call clip.Bytes directly and drop the txt.ClipBytes facade #5638 2026-06-03 09:50:56 +00:00
face.go Config: Centralize SQL driver identifiers in pkg/dsn #5588 2026-05-17 14:47:43 +00:00
face_fixtures.go Search: Change time_format to RFC3339 and add "edited" filter #4300 2024-05-27 09:16:21 +02:00
face_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
face_test.go AI: Rename SampleRadius to ClusterRadius and increase cap to 0.42 #5167 2025-10-26 10:08:59 +01:00
faces.go Backend: Rename /internal/tensorflow to /internal/ai 2024-07-02 08:08:14 +02:00
faces_test.go Security: Use individual preview tokens for each user account #98 2022-10-13 22:11:02 +02:00
file.go Entity: Rename Sanitize* to Clip* and string.go to clip.go 2026-06-03 09:50:56 +00:00
file_fixtures.go UX: Add batch edit dialog and API endpoints #271 #5324 2025-11-19 11:20:34 +01:00
file_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
file_json.go Go: Apply go fix modernizations across backend packages 2026-02-20 03:54:33 +01:00
file_json_test.go People: Add SubjectNames() to file entity #22 2021-09-02 11:12:42 +02:00
file_metadata.go Videos: Refactor File.UpdateVideoInfos() method #926 #3588 #3559 2023-08-18 09:12:38 +02:00
file_share.go Go: Apply go fix modernizations across backend packages 2026-02-20 03:54:33 +01:00
file_share_fixtures.go Routing: Prefix frontend UI routes with /library #840 #2466 2022-10-15 21:54:11 +02:00
file_share_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
file_share_test.go UX: Add batch edit dialog and API endpoints #271 #5324 2025-11-19 11:20:34 +01:00
file_sync.go Go: Apply go fix modernizations across backend packages 2026-02-20 03:54:33 +01:00
file_sync_fixtures.go Routing: Prefix frontend UI routes with /library #840 #2466 2022-10-15 21:54:11 +02:00
file_sync_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
file_sync_test.go UX: Add batch edit dialog and API endpoints #271 #5324 2025-11-19 11:20:34 +01:00
file_test.go UX: Add batch edit dialog and API endpoints #271 #5324 2025-11-19 11:20:34 +01:00
fixtures.go API: Rename /batch/photos endpoint to /batch/photos/edit #271 2025-05-04 14:09:23 +02:00
folder.go Entity: Clip album, photo, and folder paths via shared ClipPath #5615 2026-06-03 09:34:40 +00:00
folder_album_slug.go Folders: Fix slug collision on long ASCII paths #5614 #5615 2026-05-26 02:34:48 +00:00
folder_album_slug_test.go Folders: Fix slug collision on long ASCII paths #5614 #5615 2026-05-26 02:34:48 +00:00
folder_fixtures.go People: Improve face clustering and indexing #22 2021-08-19 21:12:38 +02:00
folder_test.go Folders: Improve slug collision handling for Unicode paths #5366 2026-03-07 11:16:21 +01:00
keyword.go Go: Apply go fix modernizations across backend packages 2026-02-20 03:54:33 +01:00
keyword_cache.go UX: Add batch edit dialog and API endpoints #271 #5324 2025-11-19 11:20:34 +01:00
keyword_cache_test.go UX: Add batch edit dialog and API endpoints #271 #5324 2025-11-19 11:20:34 +01:00
keyword_fixtures.go Tests: Add fixtures 2022-03-30 18:21:14 +02:00
keyword_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
keyword_test.go UX: Add batch edit dialog and API endpoints #271 #5324 2025-11-19 11:20:34 +01:00
label.go Events: Refactor entity change notifications for sharing features #1307 2026-06-11 15:40:57 +00:00
label_cache.go Entity: Fix label homophone lookups and photo-label caching #5227 #5238 2026-03-07 11:43:25 +01:00
label_cache_test.go Entity: Fix label homophone lookups and photo-label caching #5227 #5238 2026-03-07 11:43:25 +01:00
label_fixtures.go Tests: Adapt fixtures to have valid slugs 2025-08-19 19:44:23 +02:00
label_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
label_search.go Labels: Reuse renamed label when re-added by previous name #5531 2026-05-04 21:13:57 +00:00
label_search_test.go Labels: Reuse renamed label when re-added by previous name #5531 2026-05-04 21:13:57 +00:00
label_slug.go Backend: Compact verbose comments in entity and photoprism workers 2026-05-15 17:50:08 +00:00
label_slug_test.go Labels: Reuse renamed label when re-added by previous name #5531 2026-05-04 21:13:57 +00:00
label_test.go Events: Refactor entity change notifications for sharing features #1307 2026-06-11 15:40:57 +00:00
lens.go Events: UID-only camera/lens/country payloads & people refetch #5663 #1307 2026-06-15 11:37:26 +00:00
lens_cache.go Metadata: Add Camera Make and Model updates via CLI & API #5663 #5656 2026-06-15 09:25:44 +00:00
lens_fixtures.go Metadata: Add Lens Make and Model updates via CLI & API #5644 #5656 2026-06-15 09:37:32 +02:00
lens_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
lens_models.go Cards View: Improve camera and lens information #2040 #3077 #3816 2023-10-13 11:35:43 +02:00
lens_test.go Events: UID-only camera/lens/country payloads & people refetch #5663 #1307 2026-06-15 11:37:26 +00:00
link.go Logs: Improve log messages in API and share audit paths 2026-03-08 12:53:20 +01:00
link_fixtures.go Security: Add gosec fixes with shared URL and fs validation helpers 2026-03-03 16:38:41 +01:00
link_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
marker.go Config: Centralize SQL driver identifiers in pkg/dsn #5588 2026-05-17 14:47:43 +00:00
marker_fixtures.go Backend: Move crop package to /internal/thumb/crop 2024-07-02 07:45:16 +02:00
marker_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
marker_json.go People: Add face dist to marker API JSON #22 2021-09-23 14:03:35 +02:00
marker_json_test.go People: Add SubjectNames() to file entity #22 2021-09-02 11:12:42 +02:00
marker_test.go Entity: Store created and updated timestamps with second precision 2026-05-31 01:13:27 +00:00
markers.go AI: Do not add portrait or people labels from face markers #5232 2025-10-05 04:37:20 +02:00
markers_test.go AI: Improve the generation, sorting, and filtering of labels #5232 2025-10-02 13:08:52 +02:00
mysql8_test.go Config: Centralize SQL driver identifiers in pkg/dsn #5588 2026-05-17 14:47:43 +00:00
passcode.go Auth: Refine passcode recovery code comparison 2026-06-11 09:37:40 +00:00
passcode_fixtures.go Tests: Add unit tests 2024-07-17 16:38:34 +02:00
passcode_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
passcode_json.go Security: Add gosec fixes with shared URL and fs validation helpers 2026-03-03 16:38:41 +01:00
passcode_json_test.go Tests: Add unit tests #808 #3943 2024-03-27 17:01:17 +01:00
passcode_test.go Auth: Refine passcode recovery code comparison 2026-06-11 09:37:40 +00:00
password.go Entities: Improve Stringer interface implementations for logging #4490 2024-08-29 10:35:18 +02:00
password_fixtures.go Tests: Add unit tests #4973 2025-05-02 18:39:40 +02:00
password_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
password_test.go Tests: Use valid fixture IDs so entity tests pass on MariaDB 2026-05-31 00:23:23 +00:00
photo.go Index: Improve support for metadata in XMP sidecar files #2260 #2828 #5563 2026-06-17 01:53:28 +02:00
photo_album.go Docs: Improve code comments in internal/entity/photo*.go 2025-10-02 15:52:05 +02:00
photo_album_fixtures.go Tests: Fix typo in test fixtures 2025-12-02 11:02:05 +01:00
photo_album_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
photo_album_test.go Tests: Use valid fixture IDs so entity tests pass on MariaDB 2026-05-31 00:23:23 +00:00
photo_caption.go Docs: Improve code comments in internal/entity/photo*.go 2025-10-02 15:52:05 +02:00
photo_caption_test.go Tests: Add unit tests 2025-07-22 13:01:22 +02:00
photo_datetime.go UX: Add batch edit dialog and API endpoints #271 #5324 2025-11-19 11:20:34 +01:00
photo_datetime_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
photo_estimate.go Config: Centralize SQL driver identifiers in pkg/dsn #5588 2026-05-17 14:47:43 +00:00
photo_estimate_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
photo_fixtures.go Tests: Fix typo in test fixtures 2025-12-02 11:02:05 +01:00
photo_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
photo_interface.go Albums: Remove photo from review when adding it to an album #4229 2024-05-09 16:00:53 +02:00
photo_keyword.go UX: Add batch edit dialog and API endpoints #271 #5324 2025-11-19 11:20:34 +01:00
photo_keyword_fixtures.go Correct KeywordID's with extra Zero 2024-10-27 11:13:03 +01:00
photo_keyword_test.go UX: Add batch edit dialog and API endpoints #271 #5324 2025-11-19 11:20:34 +01:00
photo_label.go Entity: Fix photo-label caching #5227 #5238 2026-03-07 11:48:07 +01:00
photo_label_test.go Tests: Use valid fixture IDs so entity tests pass on MariaDB 2026-05-31 00:23:23 +00:00
photo_location.go Places: Fix minor GPS boundary overshoots in S2 mapping #5445 2026-02-10 17:29:51 +01:00
photo_location_test.go Places: Update entity/photo_location_test.go #5445 2026-02-10 17:36:47 +01:00
photo_merge.go Config: Centralize SQL driver identifiers in pkg/dsn #5588 2026-05-17 14:47:43 +00:00
photo_merge_test.go Lint: Resolve gosec findings and add embedding dist benchmark 2026-03-03 17:17:49 +01:00
photo_optimize.go UX: Add batch edit dialog and API endpoints #271 #5324 2025-11-19 11:20:34 +01:00
photo_optimize_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
photo_quality.go Media: Improve layered TIFF & PSD preview support #5383 2026-03-06 18:14:48 +01:00
photo_quality_test.go Media: Improve layered TIFF & PSD preview support #5383 2026-03-06 18:14:48 +01:00
photo_reset.go CLI: Add "photoprism vision reset" command to reset metadata #5233 2025-09-29 18:59:52 +02:00
photo_reset_test.go CLI: Add "photoprism vision reset" command to reset metadata #5233 2025-09-29 18:59:52 +02:00
photo_scope_test.go Photos: Reduce identifying metadata for shared-only sessions #1307 2026-05-29 08:20:26 +00:00
photo_test.go Config: Centralize SQL driver identifiers in pkg/dsn #5588 2026-05-17 14:47:43 +00:00
photo_title.go UX: Add batch edit dialog and API endpoints #271 #5324 2025-11-19 11:20:34 +01:00
photo_title_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
photo_user.go Logs: Add package pkg/log/status to provide generic outcome constants 2025-10-21 14:42:05 +02:00
photo_vision.go AI: Add IndexedAt time to indicate when a photo was fully indexed #5167 2025-10-07 13:57:01 +02:00
photo_yaml.go Security: Add gosec fixes with shared URL and fs validation helpers 2026-03-03 16:38:41 +01:00
photo_yaml_test.go Tests: Add unit tests #4294 2024-05-28 12:48:24 +02:00
photos.go UX: Add batch edit dialog and API endpoints #271 #5324 2025-11-19 11:20:34 +01:00
photos_test.go API: Add /batch/photos endpoint to get metadata of selected photos #271 2025-05-03 12:43:01 +02:00
place.go Backend: Rename /internal/remote package to /internal/service 2024-07-02 08:00:27 +02:00
place_fixtures.go Tests: Refactor unit tests after uid change #4560 2024-10-21 13:16:06 +02:00
place_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
place_test.go UX: Add batch edit dialog and API endpoints #271 #5324 2025-11-19 11:20:34 +01:00
reaction.go CLI: Add "photoprism vision reset" command to reset metadata #5233 2025-09-29 18:59:52 +02:00
reaction_fixtures.go Search: Change time_format to RFC3339 and add "edited" filter #4300 2024-05-27 09:16:21 +02:00
reaction_test.go Auth: Rename database tables and delete temporary tables #98 #782 2022-10-02 22:09:02 +02:00
README.md Entity: Document VARBINARY index-prefix limit and byte-exact paths #5614 #5615 2026-06-01 11:42:59 +00:00
service.go Backend: Clip VARBINARY text columns by bytes to avoid write errors #5638 2026-06-03 08:04:40 +00:00
service_fixtures.go Search: Change time_format to RFC3339 and add "edited" filter #4300 2024-05-27 09:16:21 +02:00
service_test.go Entity: Store created and updated timestamps with second precision 2026-05-31 01:13:27 +00:00
src.go Faces: Change search to return all face markers, regardless of src #5167 2025-10-07 20:01:43 +02:00
src_test.go Faces: Change search to return all face markers, regardless of src #5167 2025-10-07 20:01:43 +02:00
string_keyvalue.go People: Show real name instead of uid in logs #1438 #2182 2022-04-04 14:21:43 +02:00
string_map.go Security: Use individual preview tokens for each user account #98 2022-10-13 22:11:02 +02:00
string_map_test.go Security: Use individual preview tokens for each user account #98 2022-10-13 22:11:02 +02:00
subject.go Events: Refactor entity change notifications for sharing features #1307 2026-06-11 15:40:57 +00:00
subject_fixtures.go API: Update Thumb/ThumbSrc for subjects and labels #4151 2025-10-03 23:17:07 +02:00
subject_fixtures_test.go Test: Use PascalCase names for all Go subtests in /internal 2025-10-02 14:50:02 +02:00
subject_names.go People: Show real name instead of uid in logs #1438 #2182 2022-04-04 14:21:43 +02:00
subject_person.go People: Show real name instead of uid in logs #1438 #2182 2022-04-04 14:21:43 +02:00
subject_person_test.go People: Improve face thumbnails on overview page #22 2021-09-17 18:51:24 +02:00
subject_test.go Events: Refactor entity change notifications for sharing features #1307 2026-06-11 15:40:57 +00:00
subjects.go Revert "People: Add subject cache and show real name in logs #1438 #2182" 2022-04-04 08:54:03 +02:00
subjects_test.go Security: Use individual preview tokens for each user account #98 2022-10-13 22:11:02 +02:00

PhotoPrism — Database Entities

Last Updated: June 1, 2026

Overview

internal/entity holds the GORM models (Photo, File, Album, Label, Face, User, Client, Session, Service, Marker, …), their query and create/update helpers, the test fixtures (*_fixtures.go), and the migration helpers under migrate/. Models map to the database via GORM v1 (github.com/jinzhu/gorm) and are shared by the API, workers, and CLI.

Timestamps

Created and updated timestamps are stored as SQL DATETIME without fractional seconds (DATETIME_PRECISION = 0). To keep in-memory and persisted values in sync, the package sets GORM's timestamp source to second precision in db.go:

gorm.NowFunc = Now // entity.Now() == UTC().Truncate(time.Second)

Time helpers in entity_time.go:

  • UTC() — current time in UTC, full sub-second precision. Use for elapsed-time measurements, not for values that get persisted.
  • Now() — UTC truncated to whole seconds. This is what GORM writes to created_at / updated_at.
  • TimeStamp() — pointer to Now(), for nullable *time.Time columns.
  • Time(s) — parses an RFC 3339 string to a second-precision UTC time, or nil.

Implications:

  • Do not rely on sub-second ordering of persisted timestamps. Two rows created and updated within the same wall-clock second compare equal, so created_at / updated_at cannot disambiguate them. There is no monotonic auto-increment ID on UID-keyed models (e.g. Client), so there is no reliable intra-second tiebreaker — give rows distinct times when ordering must be deterministic.
  • Because both SQLite and MariaDB now receive second-precision values, timestamp behavior is identical across drivers. A timestamp assertion that passes on SQLite will pass on MariaDB.

When a test needs to prove a write advanced a timestamp, prefer one of:

  • Seed the starting value clearly in the past (e.g. Now().Add(-time.Hour)) and assert the new value is greater. This stays meaningful and distinguishes a real bump from a no-op.

  • Compare with Time.Sub() and assert the difference falls in a sane range, rather than a strict Before/After. A same-second save legitimately yields a zero delta:

    elapsed := after.Sub(before)
    assert.GreaterOrEqual(t, elapsed, time.Duration(0))
    assert.Less(t, elapsed, time.Minute)
    

Testing

Tests default to SQLite. To exercise the models against MariaDB (which is stricter and is the production database for some subsystems such as the cluster registry):

mysql < scripts/sql/reset-acceptance.sql
PHOTOPRISM_TEST_DRIVER="mysql" \
PHOTOPRISM_TEST_DSN="root:photoprism@tcp(mariadb:4001)/acceptance?charset=utf8mb4,utf8&collation=utf8mb4_unicode_ci&parseTime=true" \
go test ./internal/entity/... -count=1 -tags="slow,develop"

MariaDB strict mode rejects inserts that SQLite quietly accepts, so a test that only ran on SQLite can fail here:

  • Primary keys must be set. An empty PK ("" UID, zero ID) triggers Error 1364: Field '<col>' doesn't have a default value. Use a valid ID/UID, not a placeholder like "1234".
  • Values must fit the column. Oversized strings give Error 1406: Data too long; out-of-range integers give Error 1264: Out of range value (e.g. photo_id is INT UNSIGNED, max 4294967295).
  • UID format (see pkg/rnd/uid.go): a one-byte prefix + 6 base36 time chars + 9 base36 random, 16 chars total (p… photo, a… album, c… client, u… user, l… label). Reuse existing fixtures for foreign-key safety; use a throwaway but in-range value only where a real reference would overwrite seeded data (e.g. a synthetic photo_id so a Details row does not attach to a real photo).
  • Fixtures live in *_fixtures.go, but some join rows are created indirectly from a parent fixture's embedded slice (e.g. a photos_labels row from a Photo fixture's Labels). Verify a combination is free against the seeded database, not just the fixtures file.
  • List-style global queries (WHERE … <> '' with no per-test scope) are not isolated on the shared acceptance database: rows from other tests in the same run leak in, so a len(list) == N assertion that holds on a per-test SQLite file can fail on MariaDB.

Collation & Emoji

MariaDB's utf8mb4_unicode_ci assigns most emoji the same collation weight, so an SQL =, <>, or LIKE on a utf8mb4 column treats distinct emoji as equal (e.g. test/🪞 matches test/🎃). SQLite compares text byte-exact, so this only reproduces on MariaDB.

  • utf8mb4 columns that collapse: albums.album_title, display/name text (*_name, *_title).
  • VARBINARY columns that stay byte-exact: albums.album_slug, albums.album_filter, albums.album_path, photos.photo_path, and every *_uid. A utf8mb4 column compared against a VARBINARY column is byte-exact (the binary operand wins).

The durable fix for an identity/path column is to make it VARBINARYalbum_path is VARBINARY(1024) so it matches photos.photo_path and album_path = ? lookups are byte-exact at the database. Where a utf8mb4 column must stay, keep the SQL but re-verify the match byte-exact in Go before accepting it (see FindFolderAlbum / findFolderAlbumByPath, whose Go re-check is retained as defense-in-depth even now that album_path is VARBINARY). For self-join SQL where a Go re-check is awkward, HEX(col) = HEX(col) compares byte-exact on both MariaDB and SQLite. Legacy folder slugs drop emoji entirely (slug.Make("ins/🪞") == "ins") and long paths truncate to ClipSlug runes, so distinct folders can still collide on album_slug; folder albums are therefore deduplicated by album_filter (the byte-exact serialized path), not by slug (see query.RemoveDuplicateMoments).

VARBINARY Index Prefix Limit

InnoDB caps an index key prefix at 767 bytes on the COMPACT/REDUNDANT row formats, and only allows up to 3072 bytes on DYNAMIC/COMPRESSED. On a VARBINARY column the prefix is counted in bytes (on utf8mb4 it is counted in characters, i.e. up to 4 bytes each), so converting a long text column to VARBINARY can push an existing prefix index over the limit on older or non-DYNAMIC installs. Keep prefix indexes on long VARBINARY path/filter columns at ≤ 767 bytes; the project convention is 512 (albums.album_filter(512), albums.album_path(512)). A prefix index only narrows candidate rows — the full-column comparison stays exact — so a shorter prefix costs nothing for correctness.