feat: replace SFTP drop with Syncthing share path
Remove SFTPGo; mount event data from the Syncthing folder, watch incoming/, and name variants after the source stem. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+59
-9
@@ -210,7 +210,7 @@ def preprocess_original(paths: JobPaths) -> JobPaths:
|
||||
"""Downscale to MAX_EDGE_PX and normalize to sRGB JPEG as `original.jpg`.
|
||||
|
||||
Replaces any non-jpg original in the job dir. Side-effect free for the
|
||||
inbox copy — only mutates jobs/<id>/.
|
||||
incoming copy — only mutates jobs/<id>/.
|
||||
"""
|
||||
src = paths.original
|
||||
if not src.exists():
|
||||
@@ -371,12 +371,37 @@ def next_variant_id(manifest: dict[str, Any]) -> str:
|
||||
return f"v{i}"
|
||||
|
||||
|
||||
_UNSAFE_STEM_RE = re.compile(r"[^\w.\-]+", re.UNICODE)
|
||||
|
||||
|
||||
def sanitize_stem(name: str) -> str:
|
||||
"""Safe basename stem for variant files (no path separators / junk)."""
|
||||
stem = Path(name).stem if name else ""
|
||||
stem = stem.replace("/", "_").replace("\\", "_").strip().strip(".")
|
||||
stem = _UNSAFE_STEM_RE.sub("_", stem).strip("._")
|
||||
return stem or "photo"
|
||||
|
||||
|
||||
def variant_filename(stem: str, variant_id: str) -> str:
|
||||
return f"{sanitize_stem(stem)}_{variant_id}.png"
|
||||
|
||||
|
||||
def resolve_source_stem(manifest: dict[str, Any] | None, paths: JobPaths) -> str:
|
||||
"""Prefer manifest source_stem; fall back to original path stem."""
|
||||
if manifest:
|
||||
stored = manifest.get("source_stem")
|
||||
if isinstance(stored, str) and stored.strip():
|
||||
return sanitize_stem(stored)
|
||||
return sanitize_stem(paths.original.stem)
|
||||
|
||||
|
||||
def compose_variant(
|
||||
paths: JobPaths,
|
||||
assets: FilterAssets,
|
||||
*,
|
||||
variant_id: str,
|
||||
source: str,
|
||||
variant_stem: str,
|
||||
bg_name: str | None = None,
|
||||
bg_mode: str | None = None,
|
||||
fg_name: str | None = None,
|
||||
@@ -389,10 +414,13 @@ def compose_variant(
|
||||
If bg_name/fg_name/bg_mode/fg_mode are given (remix path) they are used
|
||||
directly. Otherwise a random working filter is picked with retries,
|
||||
exactly like make_random.py's compose_one().
|
||||
|
||||
Final file is `variants/{stem}_{variant_id}.png`; manifest id stays `vN`.
|
||||
"""
|
||||
rng = rng or random.Random()
|
||||
tmp_dir = paths.intermediates
|
||||
tmp_dir.mkdir(parents=True, exist_ok=True)
|
||||
stem = sanitize_stem(variant_stem)
|
||||
|
||||
bg_mode = bg_mode or rng.choice(assets.blend_modes)
|
||||
fg_mode = fg_mode or rng.choice(assets.blend_modes)
|
||||
@@ -421,7 +449,7 @@ def compose_variant(
|
||||
"step2": tmp_dir / f"{variant_id}_rembg_alpha.png",
|
||||
"fg_filtered": tmp_dir / f"{variant_id}_fg_filtered.png",
|
||||
"composed": tmp_dir / f"{variant_id}_composed.png",
|
||||
"final": paths.variants / f"{variant_id}.png",
|
||||
"final": paths.variants / variant_filename(stem, variant_id),
|
||||
}
|
||||
paths.variants.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
@@ -467,13 +495,19 @@ def compose_variant(
|
||||
}
|
||||
|
||||
|
||||
def process_job(job_id: str, source_path: Path, original_suffix: str) -> None:
|
||||
"""Full pipeline for a freshly ingested inbox file: preprocess, rembg
|
||||
def process_job(
|
||||
job_id: str,
|
||||
source_path: Path,
|
||||
original_suffix: str,
|
||||
*,
|
||||
source_stem: str | None = None,
|
||||
) -> None:
|
||||
"""Full pipeline for a freshly ingested incoming file: preprocess, rembg
|
||||
once, generate OUTPUT_COUNT variants, write manifest + status.
|
||||
|
||||
`source_path` must already be a private copy (jobs/<id>/original.*) —
|
||||
callers (worker.py) are responsible for copying out of inbox first, so
|
||||
the inbox file itself is never touched here.
|
||||
callers (worker.py) are responsible for copying out of incoming first, so
|
||||
the incoming file itself is never touched here.
|
||||
|
||||
Variants that hit FILTER_TIMEOUT during the normal pass are retried
|
||||
once at the end with FILTER_TIMEOUT_LONG. Filter probes in
|
||||
@@ -483,8 +517,9 @@ def process_job(job_id: str, source_path: Path, original_suffix: str) -> None:
|
||||
paths.root.mkdir(parents=True, exist_ok=True)
|
||||
paths.intermediates.mkdir(parents=True, exist_ok=True)
|
||||
paths.variants.mkdir(parents=True, exist_ok=True)
|
||||
stem = sanitize_stem(source_stem or source_path.stem)
|
||||
|
||||
write_status(paths, "processing", source_file=str(source_path.name))
|
||||
write_status(paths, "processing", source_file=str(source_path.name), source_stem=stem)
|
||||
|
||||
try:
|
||||
paths = preprocess_original(paths)
|
||||
@@ -493,6 +528,7 @@ def process_job(job_id: str, source_path: Path, original_suffix: str) -> None:
|
||||
"job_id": job_id,
|
||||
"original_file": paths.original.name,
|
||||
"rembg_file": paths.rembg.name,
|
||||
"source_stem": stem,
|
||||
"created_at": now_iso(),
|
||||
"variants": [],
|
||||
}
|
||||
@@ -509,7 +545,14 @@ def process_job(job_id: str, source_path: Path, original_suffix: str) -> None:
|
||||
for i in range(1, config.OUTPUT_COUNT + 1):
|
||||
variant_id = f"v{i}"
|
||||
try:
|
||||
entry = compose_variant(paths, assets, variant_id=variant_id, source="auto", rng=rng)
|
||||
entry = compose_variant(
|
||||
paths,
|
||||
assets,
|
||||
variant_id=variant_id,
|
||||
source="auto",
|
||||
variant_stem=stem,
|
||||
rng=rng,
|
||||
)
|
||||
manifest["variants"].append(entry)
|
||||
write_manifest(paths, manifest)
|
||||
except FilterTimeoutError as exc:
|
||||
@@ -530,7 +573,14 @@ def process_job(job_id: str, source_path: Path, original_suffix: str) -> None:
|
||||
with filter_timeout(config.FILTER_TIMEOUT_LONG):
|
||||
for variant_id in timeout_retries:
|
||||
try:
|
||||
entry = compose_variant(paths, assets, variant_id=variant_id, source="auto", rng=rng)
|
||||
entry = compose_variant(
|
||||
paths,
|
||||
assets,
|
||||
variant_id=variant_id,
|
||||
source="auto",
|
||||
variant_stem=stem,
|
||||
rng=rng,
|
||||
)
|
||||
manifest["variants"].append(entry)
|
||||
write_manifest(paths, manifest)
|
||||
except PipelineError as exc:
|
||||
|
||||
Reference in New Issue
Block a user