"""Remix: create one additional variant for an existing job, using the cached original + rembg output and user-chosen filters/blends/opacity.""" from __future__ import annotations from dataclasses import dataclass from . import pipeline from .pipeline import FilterAssets, JobPaths, PipelineError @dataclass class RemixChoice: bg_filter: str bg_blend: str fg_filter: str fg_blend: str opacity: str def build_remix_options(assets: FilterAssets) -> dict[str, list[str]]: return { "background_filters": sorted(assets.background_names), "foreground_filters": sorted(assets.foreground_names), "blend_modes": sorted(assets.blend_modes), } def create_remix_variant(job_id: str, choice: RemixChoice) -> dict: """Compose exactly one variant from explicit choices and append it to the job's meta. Raises PipelineError on failure (caller should show it to the user, no half-written manifest entries are ever created).""" stem = pipeline.sanitize_stem(job_id) paths: JobPaths = pipeline.job_paths(stem, ".jpg") if not paths.original.exists() or not paths.rembg.exists(): raise PipelineError("Original oder Rembg-Bild fehlt fuer diesen Job — Remix nicht moeglich.") assets = pipeline.load_assets() if choice.bg_filter not in assets.commands: raise PipelineError(f"Unbekannter Background-Filter: {choice.bg_filter}") if choice.fg_filter not in assets.commands: raise PipelineError(f"Unbekannter Foreground-Filter: {choice.fg_filter}") if choice.bg_blend not in assets.blend_modes or choice.fg_blend not in assets.blend_modes: raise PipelineError("Unbekannter Blend-Modus.") manifest = pipeline.read_manifest(stem) or { "job_id": stem, "original_file": paths.original.name, "rembg_file": paths.rembg.name, "source_stem": stem, "created_at": pipeline.now_iso(), "variants": [], } variant_stem = pipeline.resolve_source_stem(manifest, paths) manifest.setdefault("source_stem", variant_stem) variant_id = pipeline.next_variant_id(manifest) entry = pipeline.compose_variant( paths, assets, variant_id=variant_id, source="remix", variant_stem=variant_stem, bg_name=choice.bg_filter, bg_mode=choice.bg_blend, fg_name=choice.fg_filter, fg_mode=choice.fg_blend, opacity=choice.opacity, ) manifest["variants"].append(entry) pipeline.write_manifest(paths, manifest) return entry