Add read-only Synology Photos people filtering

This commit is contained in:
Pigway
2026-10-05 08:43:07 +08:00
parent 9e14adcb1f
commit f3d9a1ae97
12 changed files with 1405 additions and 5 deletions
@@ -18,6 +18,7 @@ from pigway_frame_flow.errors import ErrorCode as E
from pigway_frame_flow.errors import FlowError
from pigway_frame_flow.models import StrictModel
from pigway_frame_flow.providers.photo_names import path_caption
from pigway_frame_flow.providers.synology_photos import SynologyPhotos
from pigway_frame_flow.settings import secret
from pigway_frame_flow.storage.database import encode
@@ -32,6 +33,9 @@ class PhotoSourceInput(StrictModel):
synology_previews: bool = False
organized_only: bool = False
folder_caption: bool = False
people_filter: bool = False
person_ids: list[int] = Field(default_factory=list, max_length=50)
person_match: Literal["any", "all"] = "any"
credential_env: str | None = Field(
default=None, pattern=r"^FRAME_FLOW_SOURCE_[A-Z0-9_]+$"
)
@@ -62,6 +66,7 @@ class PhotoSources:
def __init__(self, db, settings, assets):
self.db, self.settings, self.assets = db, settings, assets
self.cache = {}
self.synology = SynologyPhotos(settings)
# Register provider secrets before request IDs or errors can be reflected.
for row in self.list():
if row["kind"] == "webdav" and row.get("credential_env"):
@@ -91,6 +96,19 @@ class PhotoSources:
return PhotoSourceInput.model_validate_json(row["payload"])
def save(self, source_id, source):
if source.people_filter and source.kind != "local":
raise FlowError(
E.INVALID_REQUEST,
"Person filtering requires a local Synology photo source",
)
if any(i <= 0 for i in source.person_ids) or len(set(source.person_ids)) != len(
source.person_ids
):
raise FlowError(
E.INVALID_REQUEST, "Person IDs must be unique positive integers"
)
if source.people_filter and not source.person_ids:
raise FlowError(E.INVALID_REQUEST, "Select at least one person")
if source.kind == "local":
self.local_directory(source.location)
if source.credential_env:
@@ -226,6 +244,11 @@ class PhotoSources:
cached = self.cache.get(source_id)
if cached and not refresh and time.monotonic() - cached[0] < 60:
return cached[1]
allowed = (
self.synology.paths(source.person_ids, source.person_match, refresh=refresh)
if source.people_filter
else None
)
items = []
if source.kind == "local":
directory = self.local_directory(source.location)
@@ -267,6 +290,8 @@ class PhotoSources:
if large and not preview:
continue
relative = path.relative_to(root).as_posix()
if allowed is not None and relative not in allowed:
continue
items.append(
{
"id": relative,