fix: classify unsent upload connections and omit raw photo filenames [skip ci]

This commit is contained in:
渡口浪人
2026-10-18 08:19:03 +08:00
parent d7600f8329
commit 41c6897151
15 changed files with 53 additions and 20 deletions
+2 -2
View File
@@ -126,7 +126,7 @@ def create_app(settings=None, start_worker=True):
app = FastAPI(
title="Pigway Frame Flow API",
docs_url=None,
version="0.1.13",
version="0.1.14",
lifespan=lifespan,
responses=error_responses,
)
@@ -1032,7 +1032,7 @@ def create_app(settings=None, start_worker=True):
supports_upload=True,
),
],
version="0.1.13",
version="0.1.14",
mock=settings.mock,
write_enabled=settings.write_enabled,
api_key_configured=bool(settings.api_key)
-2
View File
@@ -296,8 +296,6 @@ class DisplayService:
data, name, caption = self.photo_sources.read(
content.source_id, item["id"], target_size=size
)
if source.folder_caption:
caption = {**(caption or {}), "filename": name}
if (
source.minimum_sharpness
or source.minimum_brightness
+10 -1
View File
@@ -10,6 +10,7 @@ from collections import deque
from urllib.parse import quote
import requests
from urllib3.exceptions import MaxRetryError, NewConnectionError
from pigway_frame_flow.errors import ErrorCode as E
from pigway_frame_flow.errors import FlowError
@@ -124,7 +125,15 @@ class SafeTransport:
),
"request_error",
)
not_sent = isinstance(exc, requests.ConnectTimeout)
# A socket/DNS establishment failure precedes the HTTP write. A
# dropped established connection still has an uncertain outcome.
not_sent = isinstance(exc, requests.ConnectTimeout) or any(
isinstance(cause, MaxRetryError)
and isinstance(cause.reason, NewConnectionError)
for cause in exc.args
)
if not_sent and category == "connection_error":
category = "connect_error"
uncertain = write and not not_sent
raise FlowError(
E.WRITE_RESULT_UNKNOWN if uncertain else E.CLOUD_UNAVAILABLE,
+2 -2
View File
@@ -366,7 +366,7 @@ class SettingsView(BaseModel):
class Health(BaseModel):
status: str
version: str = "0.1.13"
version: str = "0.1.14"
class Readiness(BaseModel):
@@ -396,7 +396,7 @@ class RestoreRequest(StrictModel):
class Version(BaseModel):
product: str = "Pigway Frame Flow"
version: str = "0.1.13"
version: str = "0.1.14"
api_version: str = "v1"
commit: str = "development"
build_time: str = "unknown"
+4 -2
View File
@@ -621,7 +621,6 @@ class PhotoSources:
)
if source.folder_caption:
caption = dict(item.get("caption") or {})
caption["filename"] = item["name"]
try:
caption["people"] = " & ".join(
self.synology.item_people(item["id"], space=source.people_space)
@@ -631,7 +630,10 @@ class PhotoSources:
logging.getLogger(__name__).warning(
"Photos person labels unavailable; keeping photo caption"
)
item = {**item, "caption": caption}
item = {
**item,
"caption": {k: v for k, v in caption.items() if v} or None,
}
elif source.kind in ("google-drive", "alipan", "baidu"):
from pigway_frame_flow.providers.cloud_drives import CloudDrives
+1 -1
View File
@@ -41,7 +41,7 @@ def render_captioned_photo(
draw = ImageDraw.Draw(canvas)
values = [
" ".join(str(caption.get(key, "")).split())
for key in ("date", "people", "title", "place", "filename")
for key in ("date", "people", "title", "place")
]
text = " · ".join(value for value in values if value)
font_size = layout.font_size
+2 -2
View File
@@ -20,8 +20,8 @@ Object.assign(MESSAGES.en,{officialContentTitle:'KoKonna official widgets and su
Object.assign(MESSAGES.zh,{photoSources:'照片数据源',sourceHint:'通过 Synology Photos API 管理照片来源、人物和筛选规则。浏览与预览不会改变画框。',addSource:'添加数据源',browse:'浏览照片',randomPreview:'随机选图预览',sourceJob:'定时随机照片',noSources:'暂无照片,请添加数据源或检查目录。',sourceKind:'数据源类型',sourceLocation:'目录 / WebDAV URL',sourceLocationHint:'Synology Photos:所选空间内的目录路径,. 表示整个空间。按需获取 XL 预览图,不下载原图。其他云盘使用对应目录地址或 ID。',sourceCredential:'WebDAV 凭据环境变量名',sourceCredentialHint:'仅填写变量名,禁止填写密码。管理员通过环境变量或 Secret 配置 JSON username/password。',sourceRecursive:'包含子目录',sourceImages:'可用照片',sourceBrowseHint:'按需读取目录,点击文件名才生成预览;不会下载全部原图。',previousPage:'上一页',nextPage:'下一页',sourcePreviewHint:'这是已选中照片的预览,显示时使用同一张照片。',randomPhoto:'随机照片',sourceJobHint:'每次执行重新随机选图。默认暂停;启用后会按时改变画框显示。'});
Object.assign(MESSAGES.en,{photoSources:'Photo sources',sourceHint:'Manage photo sources, people and filters through the Synology Photos API. Browsing and previewing do not change the frame.',addSource:'Add source',browse:'Browse photos',randomPreview:'Preview random photo',sourceJob:'Schedule random photos',noSources:'No photos yet. Add a source or check the folder.',sourceKind:'Source type',sourceLocation:'Folder / WebDAV URL',sourceLocationHint:'Synology Photos: folder path within the selected space; . means the whole space. Downloads XL previews on demand, not originals. Other drives use their folder URL or ID.',sourceCredential:'WebDAV credential environment name',sourceCredentialHint:'Enter a variable name, never a password. Admin configures JSON username/password via environment or Secret.',sourceRecursive:'Include subfolders',sourceImages:'Available photos',sourceBrowseHint:'Catalog loaded on demand. Click a filename to preview; originals are not downloaded in bulk.',previousPage:'Previous page',nextPage:'Next page',sourcePreviewHint:'Display uses this exact previewed photo.',randomPhoto:'Random photo',sourceJobHint:'Choose a fresh random photo each run. Paused by default; enabling changes the frame on schedule.'});
Object.assign(MESSAGES.zh,{organizedPhotos:'仅日期开头文件夹中的照片',folderCaption:'照片下方显示文件夹文字',folderCaptionHint:'底部单行显示日期、已命名人物、主题、@ 后的地点和文件名。子目录沿用最近的日期文件夹;没有日期文件夹时保留文件名,未命名人物不显示。'});
Object.assign(MESSAGES.en,{organizedPhotos:'Only photos in date-prefixed folders',folderCaption:'Show folder caption below the photo',folderCaptionHint:'One footer line shows date, named people, title, place after @ and filename. Subfolders inherit their nearest dated folder; otherwise the filename is kept. Unnamed people are omitted.'});
Object.assign(MESSAGES.zh,{organizedPhotos:'仅日期开头文件夹中的照片',folderCaption:'照片下方显示文件夹文字',folderCaptionHint:'底部单行显示日期、已命名人物、主题和 @ 后的地点。子目录沿用最近的日期文件夹;未命名人物不显示,不显示原始文件名。'});
Object.assign(MESSAGES.en,{organizedPhotos:'Only photos in date-prefixed folders',folderCaption:'Show folder caption below the photo',folderCaptionHint:'One footer line shows date, named people, title and place after @. Subfolders inherit their nearest dated folder; unnamed people and original filenames are omitted.'});
Object.assign(MESSAGES.zh,{});Object.assign(MESSAGES.en,{});
+1 -1
View File
@@ -1,3 +1,3 @@
<!doctype html>
<html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover"><meta name="color-scheme" content="light dark"><title>Pigway Frame Flow</title><link rel="stylesheet" href="/ui/style.css"></head>
<body><aside class="sidebar"><a class="brand" href="/"><span class="brand-icon">F<span>↗</span></span><span>Frame Flow<small>Pigway · Smart Frame Automation</small></span></a><nav id="nav"></nav><div class="sidebar-bottom"><small>Version 0.1.13</small></div></aside><div class="workspace"><header><div><span class="eyebrow">PIGWAY FRAME FLOW</span><h1 id="page-title"></h1></div><div class="toolbar"><select id="device" aria-label="Device"></select><button id="lang" class="icon-button" aria-label="Change language">EN</button><button id="theme" class="icon-button" aria-label="Change theme">◐</button></div></header><main id="main"></main><footer id="footer"></footer></div><div id="toast" role="status" aria-live="polite"></div><dialog id="modal"><div id="modal-content"></div></dialog><script src="/ui/i18n.js?v=people-caption-v0113"></script><script src="/ui/features.js?v=people-caption-v0113"></script><script src="/ui/extensions.js?v=people-caption-v0113"></script><script src="/ui/app.js?v=people-caption-v0113"></script></body></html>
<body><aside class="sidebar"><a class="brand" href="/"><span class="brand-icon">F<span>↗</span></span><span>Frame Flow<small>Pigway · Smart Frame Automation</small></span></a><nav id="nav"></nav><div class="sidebar-bottom"><small>Version 0.1.14</small></div></aside><div class="workspace"><header><div><span class="eyebrow">PIGWAY FRAME FLOW</span><h1 id="page-title"></h1></div><div class="toolbar"><select id="device" aria-label="Device"></select><button id="lang" class="icon-button" aria-label="Change language">EN</button><button id="theme" class="icon-button" aria-label="Change theme">◐</button></div></header><main id="main"></main><footer id="footer"></footer></div><div id="toast" role="status" aria-live="polite"></div><dialog id="modal"><div id="modal-content"></div></dialog><script src="/ui/i18n.js?v=upload-caption-v0114"></script><script src="/ui/features.js?v=upload-caption-v0114"></script><script src="/ui/extensions.js?v=upload-caption-v0114"></script><script src="/ui/app.js?v=upload-caption-v0114"></script></body></html>