feat: unify source libraries, generation plans and terminal bindings
Community checks / verify (push) Skipped
Verify Frame Flow / verify (push) Failing after 6m24s

This commit is contained in:
渡口浪人
2026-10-23 17:45:30 +08:00
parent 081d8b6ee0
commit 6277c9d563
25 changed files with 5731 additions and 45 deletions
+54 -6
View File
@@ -54,6 +54,9 @@ class DisplayService:
self.cloud_library = CloudLibrary(self)
self.recovery = RecoveryMonitor(self)
self.upload_reconciliation = UploadReconciliation(self)
from pigway_frame_flow.display.libraries import Libraries
self.libraries = Libraries(self)
self.expiration_checked: dict[str, float] = {}
self.unknown_checked: dict[str, float] = {}
# A killed write cannot be replayed safely; pre-write work can resume.
@@ -67,6 +70,14 @@ class DisplayService:
def validate(self, request, writing=True):
self.registry.get(request.device_id)
if (
writing
and isinstance(request.content, ImageContent)
and request.content.asset_id
):
self.libraries.validate_asset_binding(
request.device_id, request.content.asset_id
)
if request.options.thumbnail_plan_id:
from pigway_frame_flow.display.thumbnail_plans import get_plan
@@ -120,7 +131,14 @@ class DisplayService:
)
return existing["operation_id"]
if (
action not in ("display", "render", "render-ai", "local-upload")
action
not in (
"display",
"render",
"render-ai",
"local-upload",
"library-generate",
)
and tx.execute(
"SELECT id FROM operations WHERE device_id=? AND status='UNKNOWN' AND write_started=1 AND action!='render-ai' LIMIT 1",
(device_id,),
@@ -281,6 +299,8 @@ class DisplayService:
*,
staging=False,
source_image=None,
source_only=False,
source_size=None,
):
if isinstance(content, AutoCollageContent):
from pigway_frame_flow.extensions.auto_collage import prepare
@@ -323,10 +343,15 @@ class DisplayService:
return asset
if isinstance(content, PhotoSourceContent):
source = self.photo_sources.get(content.source_id)
display = (
self.registry.get(device_id or self.settings.device_id).device().display
)
size = frame_canvas(display, source.orientation)
if source_only:
size = source_size or (1200, 1600)
else:
display = (
self.registry.get(device_id or self.settings.device_id)
.device()
.display
)
size = frame_canvas(display, source.orientation)
import random
items = self.photo_sources.catalog(content.source_id, target_size=size)
@@ -406,6 +431,17 @@ class DisplayService:
),
),
)
if source_only:
selected = self.libraries.ingest(
content.source_id, item["id"], data, name, caption
)
attempt["status"] = "PREPARED"
if operation_id:
self.db.execute(
"UPDATE operations SET result=? WHERE id=?",
(encode({"photo_attempts": attempts}), operation_id),
)
return selected
rendered = (
render_captioned_photo(
data,
@@ -565,13 +601,20 @@ class DisplayService:
"render",
"render-ai",
"local-upload",
"library-generate",
) and self.blocked(op["device_id"]):
continue
payload = json.loads(op["payload"])
if (
op["action"] in ("display", "delete", "restore")
and op["client_id"]
in ("scheduler", "scheduler-retry", "cloud-library", "retention")
in (
"scheduler",
"scheduler-retry",
"library-terminal",
"cloud-library",
"retention",
)
and self.db.one(
"SELECT id FROM operations WHERE device_id=? AND action='display' "
"AND status='SYNCING' AND write_started=1 AND updated_at>? LIMIT 1",
@@ -633,6 +676,9 @@ class DisplayService:
def _execute(self, op, payload):
id, device_id, action = op["id"], op["device_id"], op["action"]
# A source run can generate watch versions without any frame driver.
if action == "library-generate":
return self.operation_handlers[action](op, payload)
driver = self.registry.get(device_id)
if action in self.operation_handlers:
return self.operation_handlers[action](op, payload)
@@ -787,6 +833,8 @@ class DisplayService:
get_plan(self, request.options.thumbnail_plan_id)
content = request.content
if isinstance(content, ImageContent) and content.asset_id:
self.libraries.validate_asset_binding(device_id, content.asset_id)
self.update(id, "RENDERING")
asset = None
source_image = {}