2026-10-05 00:52:39 +08:00
# Photo sources
Photo sources are independent read-only providers. They never call a frame driver.
SQLite persists source definitions and Scheduler jobs; passwords are not stored there.
The shared DisplayService handles fitting, upload, idempotency, overrides and restore.
## NAS local folder
Add the actual existing photo directory to `.env` :
```dotenv
NAS_PHOTOS_DIR=/volume2/photo
2026-10-05 07:59:48 +08:00
# Optional: existing NAS group allowed to read that folder (check its ACL).
# NAS_PHOTOS_GID=101
2026-10-05 00:52:39 +08:00
```
The path above is an example, not a detected directory. Enable the optional mount:
```sh
docker compose -f docker-compose.yml -f deploy/docker-compose.photos.yml config -q
docker compose -f docker-compose.yml -f deploy/docker-compose.photos.yml up -d
```
The folder is mounted **read-only** at `/photos` . The existing container user must
2026-10-05 07:59:48 +08:00
already have read/traverse permission. If the Synology ACL grants a group access,
set `NAS_PHOTOS_GID` to that existing group ID; the optional Compose overlay adds
only container group membership, keeping UID 10333 and the photo bind mount read-only.
Group 101 is a deployment example, not a universal default. Frame Flow does not
change NAS permissions. Unreadable roots return an explicit HTTP 403 rather than
an empty catalog.
2026-10-05 00:52:39 +08:00
Missing host folders fail explicitly rather than being created automatically.
Open **Gallery → Photo sources → Add source** , choose **NAS local folder** ,
enter a relative folder (`.` for root or `family` for a subfolder).
Supported formats: JPEG, PNG, WebP. Optional recursion excludes hidden directories,
Synology `@eaDir` thumbnail trees, and symlinks. Original files remain unchanged.
## WebDAV
Open Settings and allow the server hostname under Image URL hosts.
Add a WebDAV source using a plain HTTPS directory URL on port 443.
The implementation uses read-only `PROPFIND Depth: 1` and `GET` , following
[RFC 4918 ](https://www.rfc-editor.org/rfc/rfc4918#section-9.1 ).
This version lists images immediately inside one folder; recursive WebDAV traversal
is not supported. Basic authentication is supported; OAuth, SSO and Digest are not.
Redirects, private-address endpoints and URL-embedded credentials are rejected.
For authentication, reference an environment variable such as
`FRAME_FLOW_SOURCE_CLOUD` ; set its value to a JSON object containing `username`
and `password` , or set `FRAME_FLOW_SOURCE_CLOUD_FILE` to a mounted Secret file.
Compose's env_file supplies the variable; a Secret file belongs in the existing
`/config` mount with restricted permissions and container read access. Never commit
credentials. The UI/API stores only the variable name.
## Use
- **Browse photos** returns paginated metadata (50 items/page), without fetching originals.
- **Preview random photo** imports one selected photo into local Frame Flow storage;
the frame remains unchanged. Display uses that exact previewed asset.
- **Schedule random photos** creates a device-bound Cron job, paused by default.
Each execution picks a random photo; ordinary random selection can repeat.
Editing the job supports other schedule types, fitting/profile via advanced JSON,
and the existing temporary override/restore options.
- Sources can be edited, disabled or removed. Remove referencing jobs before deletion.
Metadata is cached for 60 seconds, with explicit refresh. Each catalog is limited
to 10,000 supported images (not a complete inventory for larger folders).
Maximum selected image size is 20 MiB; WebDAV XML responses are limited to 2 MiB.
Preview imports use the existing local asset store; monitor disk space over time.
Unreadable/invalid images fail the operation without retrying frame writes.
## API
- `GET /api/v1/photo-sources`
- `PUT /api/v1/photo-sources/{id}` (admin)
- `DELETE /api/v1/photo-sources/{id}` (admin)
- `GET /api/v1/photo-sources/{id}/images?offset=0&limit=50&refresh=false`
Display or Scheduler content:
```json
{ "type" : "photo_source" , "source_id" : "family" , "fit" : "contain" , "profile" : "color" }
```
Optional `item_id` selects an exact catalog item. Omit it for random selection.
The device upload endpoint accepts the same content with `display_now=false`
(default) to import/preview locally. `POST /api/v1/display` performs the usual
protected asynchronous device display. Schemas are in `/openapi.json` and `/docs` .
Native Aliyun Drive/Baidu Drive/Google Drive adapters are not implemented.
A compatible WebDAV gateway or an independently managed NAS sync folder can be used.
Frame Flow does not install a gateway or receive vendor API credentials automatically.
2026-10-05 01:36:05 +08:00
## Date-folder captions / 整理照片文字
Enable **Only photos in date-prefixed folders** and **Show folder caption below
the photo** when editing a source. Curated sources created by the UI increase the
catalog limit to 100,000 photos; other sources default to 10,000. The API exposes
`catalog_limit` explicitly. It remains a bounded catalog; reaching the limit is
shown in the UI.
Example folder name: `20261005 家庭出游@示例地点` .
The image contains the photo and three separate caption lines:
```text
家庭出游
2026-10-05
示例地点
```
Without `@` , the location line is omitted. Folder names support `YYYYMMDD` ,
`YYYY-MM-DD` , `YYYY.MM.DD` , and `YYYY年MM月DD日` , with calendar-valid dates.
Subfolders inherit their nearest dated ancestor; image filenames and EXIF do not
replace this curated date. Synology `#recycle` , hidden folders, and `@eaDir` are
excluded. Extremely long captions shrink and then truncate visually; full values
remain available in catalog metadata.
Output is 1200× 1600: a 1200× 1280 photo area plus a white caption area. Original
files remain unchanged. Contain/cover/smart and color/grayscale profiles still apply.
A prepared source definition is in `deploy/organized-photos-source.json` ; use it
with `PUT /api/v1/photo-sources/organized-photos` after mounting the library.
2026-10-05 08:18:56 +08:00
For Synology Photos shared libraries, `synology_previews: true` allows images over
20 MiB to use their existing `@eaDir/<filename>/SYNOPHOTO_THUMB_XL.jpg` preview.
This is a read-only fallback: original files and Photos indexes remain unchanged,
no thumbnails are generated on the NAS, and the ordinary size limit still applies
to the selected preview. The catalog marks these entries `provider_preview: true` .
If the preview is missing, the oversized original remains excluded. The folder
caption still comes from the original curated directory.
2026-10-05 08:43:07 +08:00
## Synology Photos 人物筛选 / People filtering
可选的只读集成使用 Synology Photos **共享空间**的人物识别结果,不运行新的识别模型,也不修改人物分组或照片。个人空间暂不支持。
1. 将共享图库(如 `/volume1/photo` )挂载为 `/photos` ,保持只读。
2. 在 `.env` 配置 `SYNOLOGY_PHOTOS_URL=https://NAS_IP:9115` 和 `SYNOLOGY_PHOTOS_CERT_SHA256` (服务实际 TLS 证书的 SHA-256 指纹,从可信 NAS 配置核实)。重建容器后生效。指纹变化时必须重新核实;每次连接在发送登录信息前校验同一个 TLS socket 的证书。
2026-10-05 09:09:18 +08:00
3. 照片数据源 → 编辑数据源 → 连接 / 选择人物。管理员输入 NAS 用户名、密码;只保存 session 到 `/config/synology-photos-session.json` (0600),不保存密码。会话过期后需要重新连接;断开连接删除本地 session。页面没有 password/token 回显。
2026-10-05 08:43:07 +08:00
4. 选择人物,启用筛选,选择“任意”或“同时包含所有”。默认只列出已命名人物,可搜索 ID、展开未命名分组。至少选择一个人物。
5. 与当前本地目录、日期文件夹规则取交集,再随机选图。原文件夹标题、日期、地点仍保留。不匹配返回空图库;预览 / 定时任务失败,不回退到全图库。
接口:`GET/POST/DELETE /api/v1/integrations/synology-photos` (管理员);`GET /api/v1/integrations/synology-photos/people` (管理员)。Source 新增 `people_filter` 、`person_ids` 、`person_match: any|all` 。普通图片读取权限只能使用已配置数据源,不能登录或查询人物目录。
只允许 Auth login/logout、共享空间 Person.list、Item.list、Folder.get。人物照片按 500 条分页,成员路径缓存 15 分钟,源目录缓存 60 秒;显式刷新会重新查询。无后台高频轮询,不下载全图库原图。查询失败和登录失效明确报错。大型人物相册首次查询可能需要数十秒。
This optional integration reads the existing shared-space People catalog. It does not modify Photos or perform face inference. Login stores only a protected session, never the NAS password. Certificate pinning happens before credentials are sent. Select one or more people and match any/all, intersect with the existing curated folder rules, then randomize. Empty matches and expired sessions fail closed. Person membership caches for 15 minutes; explicit refresh re-queries. This improves relevance, not blur/closed-eye detection. Recognition can miss faces. Personal-space filtering and automatic photo-quality scoring are not implemented.
2026-10-05 10:38:20 +08:00
## 照片方向 / Orientation
编辑照片数据源可选:横向、竖向、自动识别。自动匹配当前渲染画布 1200×1600,因此选竖向。按 EXIF 校正后的 width/height 分类,正方形不混入横向或竖向。预览、指定图片和定时随机选图使用同一筛选结果;空结果明确失败,不退回其他方向。照片不为匹配方向强制旋转或拉伸。
已有数据源 `orientation=null` 保持原来的全部方向,编辑器显示“不筛方向(原有设置)”;新建数据源默认自动。尺寸缓存持久化在 SQLite `photo_dimensions` ,按路径、修改时间和大小失效。首次扫描读取本地图片尺寸和 EXIF,不批量生成全图库预览;后续刷新复用缓存。仅 NAS 本地源支持,WebDAV 不批量下载原图测尺寸。若将来改变原生画布尺寸,需要同步调整 auto 的匹配规则。
Orientation options: landscape, portrait, auto. Auto matches the current portrait rendering canvas. Classification honors EXIF orientation; square photos are excluded from directional matches. Existing sources remain unfiltered (`null` ) until changed. Dimensions persist in SQLite and refresh when the file changes. Local NAS sources only; no bulk original downloads from WebDAV. Empty matches fail closed. This controls selection, not photo rotation or stretching.