Render curated date-folder photos with title date and place captions

This commit is contained in:
Pigway
2026-10-05 01:36:05 +08:00
parent f5ab1ade77
commit 011339d511
15 changed files with 386 additions and 18 deletions
+31 -7
View File
@@ -17,6 +17,7 @@ from pydantic import Field
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.settings import secret
from pigway_frame_flow.storage.database import encode
@@ -27,6 +28,9 @@ class PhotoSourceInput(StrictModel):
location: str = Field(min_length=1, max_length=2048)
recursive: bool = True
enabled: bool = True
catalog_limit: int = Field(default=10000, ge=1, le=100000)
organized_only: bool = False
folder_caption: bool = False
credential_env: str | None = Field(
default=None, pattern=r"^FRAME_FLOW_SOURCE_[A-Z0-9_]+$"
)
@@ -39,6 +43,7 @@ class PhotoSourceView(PhotoSourceInput):
class PhotoItem(StrictModel):
id: str
name: str
caption: dict[str, str] | None = None
class PhotoCatalog(StrictModel):
@@ -204,13 +209,19 @@ class PhotoSources:
sorted(
d
for d in dirs
if not d.startswith(".")
if d != "#recycle"
and not d.startswith(".")
and not d.startswith("@")
and not Path(folder, d).is_symlink()
)
if source.recursive
else []
)
caption = path_caption(
Path(folder).relative_to(root).as_posix() + "/__photo__.jpg"
)
if source.organized_only and caption is None:
continue
for name in sorted(files):
path = Path(folder, name)
if (
@@ -223,12 +234,18 @@ class PhotoSources:
if path.stat().st_size > self.settings.max_bytes:
continue
relative = path.relative_to(root).as_posix()
items.append({"id": relative, "name": name})
items.append(
{
"id": relative,
"name": name,
"caption": caption if source.folder_caption else None,
}
)
except OSError:
continue
if len(items) >= 10000:
if len(items) >= source.catalog_limit:
break
if len(items) >= 10000:
if len(items) >= source.catalog_limit:
break
else:
data = self.assets.fetch_url(
@@ -266,12 +283,19 @@ class PhotoSources:
or parsed.fragment
):
continue
caption = path_caption(unquote(parsed.path))
if source.organized_only and caption is None:
continue
if Path(parsed.path).suffix.lower() in self.extensions:
seen.add(url)
items.append(
{"id": url, "name": unquote(Path(parsed.path).name)}
{
"id": url,
"name": unquote(Path(parsed.path).name),
"caption": caption if source.folder_caption else None,
}
)
if len(items) >= 10000:
if len(items) >= source.catalog_limit:
break
except ElementTree.ParseError:
raise FlowError(E.INVALID_REQUEST, "Invalid WebDAV catalog") from None
@@ -332,4 +356,4 @@ class PhotoSources:
data = self.assets.fetch_url(item["id"], headers=self.headers(source))
if len(data) > self.settings.max_bytes:
raise FlowError(E.INVALID_REQUEST, "Image exceeds size limit")
return data, item["name"]
return data, item["name"], item.get("caption")