Add advanced canvas editing and Ubuntu deployment
This commit is contained in:
+68
-17
@@ -885,27 +885,76 @@ async def upload_asset(
|
||||
return Asset(**meta)
|
||||
|
||||
|
||||
@app.post("/api/assets/from-job/{job_id}", response_model=Asset)
|
||||
async def import_asset_from_job(
|
||||
job_id: str,
|
||||
name: str = Form(""),
|
||||
) -> Asset:
|
||||
# 尝试从内存获取产物路径;若服务已重启则从磁盘回退
|
||||
|
||||
def _svg_with_transparent_background(svg_bytes: bytes) -> bytes:
|
||||
"""Convert full-canvas opaque backgrounds to transparent for sticker use.
|
||||
|
||||
Job pipeline SVG (`to_svg`) embeds a white full-rect so standalone preview
|
||||
looks solid. Canvas stickers must stay transparent so the mask layer shows
|
||||
through. Only the full-canvas background rect is touched.
|
||||
"""
|
||||
try:
|
||||
text = svg_bytes.decode("utf-8")
|
||||
except UnicodeDecodeError:
|
||||
text = svg_bytes.decode("utf-8", errors="ignore")
|
||||
|
||||
patterns = [
|
||||
# <rect width="100%" height="100%" fill="white"/>
|
||||
(
|
||||
r'(<rect\b[^>]*\bwidth=["\']100%["\'][^>]*\bheight=["\']100%["\'][^>]*\bfill=["\'])(?:white|#fff(?:fff)?|rgb\(\s*255\s*,\s*255\s*,\s*255\s*\))(["\'][^>]*?/?>)',
|
||||
r'\1none\2',
|
||||
),
|
||||
# attribute order: fill before width/height
|
||||
(
|
||||
r'(<rect\b[^>]*\bfill=["\'])(?:white|#fff(?:fff)?|rgb\(\s*255\s*,\s*255\s*,\s*255\s*\))(["\'][^>]*\bwidth=["\']100%["\'][^>]*\bheight=["\']100%["\'][^>]*?/?>)',
|
||||
r'\1none\2',
|
||||
),
|
||||
]
|
||||
for pattern, repl in patterns:
|
||||
updated, n = re.subn(pattern, repl, text, count=1, flags=re.IGNORECASE)
|
||||
if n:
|
||||
return updated.encode("utf-8")
|
||||
return svg_bytes.encode("utf-8") if isinstance(svg_bytes, str) else svg_bytes
|
||||
|
||||
|
||||
def _find_job_svg(job_id: str) -> Path | None:
|
||||
"""Locate the main (non-stroke) SVG for a job.
|
||||
|
||||
Prefer in-memory artifact path when valid; always fall back to the job
|
||||
workspace on disk so empty/stale memory state cannot hide existing files.
|
||||
"""
|
||||
svg_path: Path | None = None
|
||||
|
||||
if manager.exists(job_id):
|
||||
status = manager.get_status(job_id)
|
||||
svg_path_str = status.artifacts.get("svg", "")
|
||||
if svg_path_str:
|
||||
svg_path = Path(svg_path_str)
|
||||
else:
|
||||
# 服务重启后内存丢失,直接从工作区目录查找
|
||||
fallback_dir = storage.base_dir / job_id / "output"
|
||||
if fallback_dir.exists():
|
||||
for candidate in fallback_dir.glob("*.svg"):
|
||||
candidate = Path(svg_path_str)
|
||||
if candidate.exists():
|
||||
svg_path = candidate
|
||||
break
|
||||
|
||||
if svg_path is not None:
|
||||
return svg_path
|
||||
|
||||
fallback_dir = storage.base_dir / job_id / "output"
|
||||
if not fallback_dir.exists():
|
||||
return None
|
||||
|
||||
candidates = sorted(fallback_dir.glob("*.svg"))
|
||||
for candidate in candidates:
|
||||
if candidate.name.endswith("_stroke.svg"):
|
||||
continue
|
||||
return candidate
|
||||
return candidates[0] if candidates else None
|
||||
|
||||
|
||||
@app.post("/api/assets/from-job/{job_id}", response_model=Asset)
|
||||
async def import_asset_from_job(
|
||||
job_id: str,
|
||||
name: str = Form(""),
|
||||
type: str = Form("wordcloud"),
|
||||
) -> Asset:
|
||||
svg_path = _find_job_svg(job_id)
|
||||
if svg_path is None or not svg_path.exists():
|
||||
raise HTTPException(status_code=404, detail="svg not found")
|
||||
|
||||
@@ -914,16 +963,18 @@ async def import_asset_from_job(
|
||||
asset_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
dest = asset_path / "asset.svg"
|
||||
shutil.copy2(svg_path, dest)
|
||||
|
||||
content = dest.read_bytes()
|
||||
raw = svg_path.read_bytes()
|
||||
# 画布贴纸需要透明底;任务主 SVG 默认带白底 rect
|
||||
content = _svg_with_transparent_background(raw)
|
||||
dest.write_bytes(content)
|
||||
width, height = _parse_svg_viewbox(dest)
|
||||
|
||||
asset_type = (type or "wordcloud").strip() or "wordcloud"
|
||||
display_name = name or f"词云_{job_id[:8]}"
|
||||
meta = {
|
||||
"asset_id": asset_id,
|
||||
"name": display_name,
|
||||
"type": "wordcloud",
|
||||
"type": asset_type,
|
||||
"mime_type": "image/svg+xml",
|
||||
"width": width,
|
||||
"height": height,
|
||||
|
||||
Reference in New Issue
Block a user