Commit Graph
1213 Commits
Author SHA1 Message Date
digital-garbageandClaude 70cd015219 Easy Gen: prompt $name variables panel
Ports the Editor Composer's $variables to Easy Gen: a "$ Variables" toggle in
the header opens a collapsible name/value editor docked above the prompt bar.

Same project.variables field the Editor writes, so nothing backend-side is new —
movie_editor/server.py already forwards it on every generate and Studio resolves
it dead-last inside refine_v2 (after shortcut expansion AND after the scene
split), which is what makes a $var work inside a shortcut's replacement while a
value containing a comma or trigger word can never move a scene cut.

- easy_store: projectVariables/setProjectVariables (leading $ stripped on store)
- easy_variables.js: rows + Add/Remove, count badge, live hint for undeclared
  names and self-referencing loops (cycle detection mirrors resolve_variables)
- rows rebuild only on add/remove/project-change, so typing never loses focus

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-26 20:11:38 +03:00
digital-garbage e29f7fea3b Refresh Hub screenshot with a clean git status 2026-07-24 12:27:00 +03:00
digital-garbage 62606d694e README: document the Hub and the two UI variants, with screenshots
Adds a Hub screenshot and a Cutting Room / Easy Gen side-by-side comparing
what each is for, plus a short 3.4.0 callout in the version history section.
2026-07-24 12:25:35 +03:00
digital-garbage f1a376e286 Remove dead "Seed" field from Easy Gen settings; bump to 3.4.0
The Generation panel's Seed field was bound to Project.seed, a legacy field
(movie_editor/backend/timeline.py:562) that nothing in generation reads —
the real seed resolution (server.py's _resolve_run_seed) only ever reads
sampler_inputs.seed, which already defaults to random when unset. The field
always showed "1" and editing it had zero effect, reading as "seed is
stuck, not randomized" when generation was in fact already randomizing
correctly via the real (and already-present) Seed control under Engine ▸
Timing & Seed. Removed the dead field; the settings hint now points there.

Also bumps pyproject.toml to 3.4.0 and adds the CHANGELOG entry for this
session's Easy Gen work (shortcut/transition import, Engine settings, Save/
Gallery/Export buttons, continuity pin, and the bypass/state-sync/
autoplay/filename fixes), ahead of tagging the release on main.
2026-07-24 12:19:49 +03:00
digital-garbage fadfc9626d Add continuity pin button to Easy Gen's Gallery
Mirrors mediabrowser.js's card pin button in the Editor: a 📌 button on
each image thumbnail (chain-sampler pipelines only) toggles
continuity_settings.identity_pin_ref, re-enabling auto_enabled if it was
off (a pin does nothing with auto continuity disabled). Same setting as
Engine settings > Continuity > Identity pin, just reachable without
leaving the Gallery. Verified both surfaces read/write the same
continuity_settings and stay in sync.
2026-07-24 11:47:38 +03:00
digital-garbage 99c7dd5ba6 Add project Export button to Easy Gen
Easy Gen's "Save" persists into FunPack's own project store on that
machine, same as the Editor's autosave — it was never a way to move a
project between machines. The backend route for that (download the actual
.funpack_project.json, re-importable via "Load Project File...") already
existed and easy_gen/frontend/api.js already had the URL helper, but no
button anywhere called it, unlike the Editor's File > "Save Project File...".
Added an "Export..." button next to Save that flushes a save first, then
downloads the project file.
2026-07-24 11:29:58 +03:00
digital-garbage 6a58b35f6f Fix Easy Gen silently reverting Models & Pipeline edits; two smaller fixes
Root cause of "bypass I just disabled is still bypassed after reopening
settings": models.js saves bypass/expose/widget edits straight to the
server via its own PUT /projects/{id}/models, entirely independent of Easy
Gen's state.project. Any later save() (prompt debounce, the Save button, an
upload) sent the STALE in-memory project.models back to the server and
silently clobbered whatever was just changed there — same class of bug the
Editor's store.js already guards against by syncing state.models into
project.models before every autosave commit, which Easy Gen never got.
Fixed by refetching the authoritative models config at the top of every
save() call. Verified live: toggling bypass via the Models API then calling
Store.save() no longer reverts it.

Also:
- preview.js: video elements used the `autoplay` attribute, which makes the
  browser call .play() internally with no promise we can catch. Replacing
  the element before that settles (e.g. picking a new Gallery item) surfaced
  as an unhandled "fetching process...aborted" rejection banner. Now calls
  .play() ourselves so we own (and can catch) the promise.
- server.py: the /result route had no Content-Disposition, so "Save Video
  As" fell back to a generic name derived from the URL path ("result.mp4")
  every time. The underlying render file is already uniquely named per run
  and already lives in ComfyUI's temp dir (cleared on restart) — just
  wasn't surfaced to downloads. Now sets Content-Disposition with the real
  per-run filename on all three /result response paths.
2026-07-24 11:23:23 +03:00
digital-garbage 96ba54f17d Fix bypass toggle silently doing nothing when the mapping is ambiguous
_apply_bypass() only skips a bypassed node's effect when it can find exactly
one connection-input matching each of its output types (and that input is
actually wired to something). When either condition failed — e.g. an i2v
preprocessing node with more than one output type, or a MASK/IMAGE pair with
no unambiguous 1:1 match — it silently left the node fully active and only
recorded a reason in report["unsatisfied"], which no frontend code ever
reads or displays. A user toggling bypass got no error and no bypass: the
node just kept running as if untouched.

Both failure paths now also add to report["blocking"], which server.py's
generate route already rejects with a clear "Generation blocked — ..."
error shown to the user. A bypass a user explicitly asked for now either
happens or the run stops and says why — never a silent no-op.

Updated the test that asserted the old (buggy) "leaves it active" behavior
to assert the new blocking behavior instead.
2026-07-24 11:14:51 +03:00
digital-garbage d21026b2a6 Fix shortcut autocomplete menu opening off-screen below the field
position() always placed the suggestion menu below the textarea. Easy Gen's
prompt box lives in a footer bar at the bottom of the viewport, so the menu
opened mostly or entirely off-screen with no way to see or click a
suggestion. Now flips above the field when there isn't room below, and
clamps max-height to whatever space is actually available either way.
2026-07-24 11:04:56 +03:00
digital-garbage c2d1257a8e Easy Gen: add Log button, stop preview auto-resuming, hide inert eye buttons
- Add the Cutting Room's "Log" button/panel to Easy Gen (log.js is already
  fully self-contained and hits the same shared /api/log route Easy Gen's
  api.js already calls) — just needed the script tag and a .menubar-right
  wrapper for it to mount into.
- Fix: any Store notify (health poll every 15s, prompt debounce) rebuilt the
  preview's <video>/<img> from scratch, reapplying autoplay/loop and silently
  resuming anything the user had paused. draw() now skips the rebuild when
  the actual content being shown hasn't changed.
- Hide the "expose to editor" eye buttons (both plain widgets and the bypass
  toggle) in Models & Pipeline when running under Easy Gen — there's no main
  editor / per-scene inspector for an exposed control to ever appear in, so
  they were inert clutter that could look actionable and silently do
  nothing. Gated on window.FunPackAppName, same discriminator engine_settings.js
  and sampler_panel.js already use; Movie Editor is unaffected.
2026-07-24 11:02:04 +03:00
digital-garbage cfe606fb95 Force Easy Gen into a rating-free Studio Prompt-only pipeline
Easy Gen has no rating UI and won't get one — so every Engine setting that
is a no-op without a trained refinement key/rated history is now hidden
there (gated on window.FunPackAppName, the existing Easy-Gen discriminator),
not just harder to find: refinement key, value guidance, steer mode,
reference injection, embed/score/output guidance, taste retrieval,
DynaShift, and sampler-panel velocity bias/rescue. A note points users to
the Cutting Room or the ComfyUI graph for the full learned refiner.

Also enforced at the pipeline level, not just the UI: every Easy Gen
generate call now forces the Studio node into "Prompt only" mode and the
rating-gated Chain Sampler knobs off via node_overrides, so a project
edited in the Cutting Room with these enabled still generates plainly when
run from Easy Gen. Movie Editor is untouched — FunPackAppName is unset
there, so every added gate evaluates false.
2026-07-24 10:50:36 +03:00
digital-garbage d4236be5fb Easy Gen: shortcut/transition library import, Engine settings, Save, Gallery
Easy Gen shipped shortcuts/transitions and multi-scene splitting with no way
to load a shortcut/transition JSON library, no path to Engine Settings
(blocking Generate on stale continuity media refs, no Best-FaceID toggle),
no explicit Save, and no way to reuse an already-uploaded file as an anchor.

- Shortcut/transition JSON import/export in the Shortcuts settings section.
- Full Engine Settings (Studio/Chain Sampler/Continuity/Best-FaceID) now
  loads via the same frontend-fallback as autocomplete.js, backed by new
  patchProject/setSamplerInput*/setStudioInput*/mediaBin support in
  easy_store.js. Removed the now-duplicate Refinement key field from the
  slim Generation panel.
- New Gallery picker (easy_gallery.js) to apply prior uploads as the anchor.
- Explicit Save button with Saving…/Saved feedback.
2026-07-24 10:37:40 +03:00
digital-garbage 0191f71606 Add prompt autocomplete, splits, anchor, and shortcut revolver to Easy Gen
The one prompt textarea is now the verbatim "global prompt" and silently
splits into N scenes via the same backend parser the Editor's Composer
uses (POST /projects/{id}/parse) — no per-scene UI is ever shown, and
Generate already runs the whole project as one chain request (no
scene_ids filter), so N scenes still come back as one stitched video.

- easy_store.js: applyGlobalPrompt()/scheduleGlobalPromptApply() port
  the Editor's split+anchor-fold logic (_normalizeGlobalPromptParse,
  _inferSplitMarkers), sequence-guarded against a stale parse response
  racing a newer keystroke. Scene 0 keeps its uploaded source across
  re-splits; scenes 1..N default to "carry" (continue the chain).
  Also added getEditorSetting/setEditorSetting (same localStorage key
  as the Editor) and loadLibraries() (shortcuts + transitions).
- easy_shortcuts_settings.js (new): "Shortcuts" settings section —
  autocomplete + anchor toggles, plus the shortcut revolver (server-side,
  shared with the Editor and with generation regardless of which UI
  queued the run). Skips the Editor's "ideas" bulb and its custom-node
  anchor-guide bypass section, neither relevant here.
- app.js: prompt box now drives scheduleGlobalPromptApply() instead of
  editing scene 0 directly, displays project.global_prompt, and attaches
  ShortcutAutocomplete.

Verified live: multi-scene split + anchor fold match real project data,
upload survives a re-split, autocomplete fires against the real shortcut
library, revolver toggle round-trips, and Generate submits the whole
multi-scene project correctly (fails at the same "missing pipeline
nodes" point as before on this bare dev install — no regression).
2026-07-23 17:27:03 +03:00
digital-garbage fe45c422ce Show uploaded media in Easy Gen preview, with a close/remove button
Preview now shows the uploaded image/video (badged "Uploaded source")
when there's no generated result yet, or the generated result itself
with a corner ✕ to dismiss it and reveal the upload underneath.

Fixed two real bugs found while testing this:
- Upload never actually attached to the scene: POST /media returns
  {media: [...]} (a list, for multi-file support), but app.js read the
  response as if it were the media object itself.
- A save-ordering race could silently lose an edit: two overlapping
  save() calls could land on the server out of order, letting an
  older save clobber a newer one. save() now serializes through one
  queue in easy_store.js, always reading live project state.
- CSS: .easy-preview-close's unconditional display:flex was beating
  the hidden attribute (equal specificity); added an explicit
  [hidden] override.
2026-07-23 16:58:28 +03:00
digital-garbage 9791cb8ccd Add FunPack Easy Gen (simplified single-scene UI) and a /funpack Hub
Easy Gen is a new Fooocus-style frontend at /funpack/easy — big preview,
one prompt box, Upload, Generate, and an Advanced settings panel — built
entirely on the Movie Editor's existing project format and API (no new
backend endpoints), so projects stay interchangeable between the two UIs.

The Hub at /funpack links to both UIs and surfaces version/branch/commit
plus an Update/Restart action reusing the Editor's git_update.py flow.

Startup now prints full host:port URLs for all three (Hub, Editor, Easy
Gen) via a new config.comfy_display_url() helper.

Also: settings_window.js's About section now labels itself via
window.FunPackAppName instead of hardcoding "Cutting Room", and Easy
Gen's boot-error banner accumulates distinct errors instead of
overwriting, so a cascading failure (e.g. dom.js not loading) surfaces
its root cause instead of just the last script in load order.
2026-07-23 16:37:26 +03:00
digital-garbageandClaude Fable 5 726d88a547 Add detail_mode: sharpen (near-free, no diffusion) vs repair (current)
Recovers the original pitch: 'cheap, on-the-fly sharpen' was quietly
upgraded into a full re-denoise pass during design without ever offering
the free version as an option. The trained latent upsampler net was
already an intermediate step in the pipeline (crop_up) — sharpen mode
just stops there and pastes it back, with ZERO video-model forward
passes (only the small upsampler CNN runs). repair mode (default,
unchanged, byte-identical behavior) keeps the existing re-noise + 3-step
tail for cases that need actual structural correction, since only the
generative model itself can decide a region should look structurally
different — a super-res net can only sharpen what's already there.

Also extends the Editor's knobVisible with a  array (AND'd
conditions) — detail_denoise needed to gate on BOTH segmented_detailing
AND detail_mode=='repair', which the existing single dependsOn/dependsValue
couldn't express without silently dropping one of the two checks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 01:26:34 +03:00
digital-garbageandClaude Fable 5 1644dde829 Expose the crop re-noise strength: fixed 0.85 was sharpening, not repairing
Live report: a hand was detected, masked, and upsampled, but the pasted
result looked like a plain upscale, not an actually-corrected region.
Root cause is mechanical, not a bug: 0.85 is Lightricks' own official
re-entry sigma, but it was designed for their use case (sharpen an
already-correct frame at higher resolution), not ours (repair a likely-
malformed crop). At that re-entry point the tail mostly smooths the
upsampled interpolation back down rather than giving the model enough
noise/freedom to genuinely reconstruct structure.

detail_denoise (0.3-0.99, default 0.85 = unchanged prior behavior) now
controls the re-entry point; the tail's shape (ratio between its 3 steps)
scales with it via the new stage2_sigmas() instead of a fixed tuple, so
raising it doesn't just enlarge the first jump. Higher = more genuine
repair freedom at some risk of drifting from the surrounding frame.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 01:19:34 +03:00
digital-garbageandClaude Fable 5 0372ee8287 Make the area cap a user knob, not a silent content judgment
The 35% area cap was refusing a user-requested 'hand' region (47% of
frame) with no way to override it — the code was unilaterally deciding
the request 'wasn't a detail pass' when the user never asked for that
judgment. Fair complaint: cost guardrails are not the same as content
decisions.

detail_max_area is now threaded end-to-end (detailing.area_cap param ->
sampler widget -> Editor knob in Chain Sampler > Experimental, 0.05-1.0,
default 0.35 unchanged). Raising it lets a region of any size through;
1.0 = no cap. The refusal note now points at the actual setting name
instead of citing a fixed constant.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 01:10:27 +03:00
digital-garbageandClaude Fable 5 30b27b734a Instrument CLIPSeg misses + expose detail_threshold knob
Live run hit 'no region matched' with zero diagnostics: CLIPSeg's raw
sigmoid score for a real, correctly-named region is often well under a
naive 0.5, so a miss against the hardcoded 0.35 threshold was
indistinguishable from 'nothing there.' Two fixes:

- find_tube now takes an optional diag dict and always records max_heat
  (plus area_cap vs below_threshold reason on a miss).
- detail_refine_scene always returns a descriptive note once detection
  actually ran ('no match: max CLIPSeg score 0.XX < threshold 0.35 — try
  a lower detail_threshold...'), replacing the generic
  'no region matched' the sampler used to fabricate.
- detail_threshold is now a real widget/sampler_input (was a hardcoded
  constant with no UI — a hidden-setting violation once real runs started
  needing it), in Engine > Chain Sampler > Experimental next to the other
  detailing knobs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 01:03:17 +03:00
digital-garbageandClaude Fable 5 5727c39189 Fix CLIPSeg multi-prompt crash: missing padding/truncation on tokenizer call
Live rental run hit it immediately on 'detail_targets' with more than one
target: CLIPSegProcessor tokenizes each prompt to a different length, and
without padding=True torch can't stack them into a batch ('excessive
nesting... type list where int is expected'). Reproduced against the real
CIDAS/clipseg-rd64-refined model before and after the fix. Single-target
runs were unaffected (batch of 1 never needed padding), which is why the
earlier probe steps didn't catch it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 00:53:36 +03:00
digital-garbageandClaude Fable 5 c864f62480 Segmented detailing: auto-resolve/download upsampler + loud skips
The first live attempt silently no-oped: detail_upsampler defaulted to 'None'
(nothing installed on the rental) and the guard skipped without a word.
Two fixes:
- detail_upsampler default is now 'auto': prefer the official installed file,
  else the highest-versioned spatial upscaler (version-aware compare — plain
  sort ranked ltx2_3_… over ltx-2.3-…-1.1), else hf_hub_download of
  Lightricks/LTX-2.3 ltx-2.3-spatial-upscaler-x2-1.1.safetensors (~1 GB, once)
  with an actionable error if the repo is gated. Legacy 'None' = auto.
- With the toggle on, every outcome is visible: upsampler choice and skips
  print to console AND land in the scene report (segmented_detail(SKIPPED: …) /
  (no region matched)); resolve/load failure disables the pass for the run
  instead of retrying the download per scene. A failed pass still never costs
  the scene.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 18:29:28 +03:00
digital-garbageandClaude Fable 5 db121973f1 Editor UI for segmented detailing: Engine knobs + Composer targets field
Engine > Chain Sampler > Experimental: segmented_detailing toggle, detail_targets
text knob (new 'text' kind: quiet patch while typing, commit on blur), detail_strength,
and a live detail_upsampler combo fed by the chain sampler's own node spec (same
lazy pattern as the ArcFace projector). Composer > Compose grows a Segmented
detailing targets field, shown only while the feature is on — same store key as the
Engine knob so the two can never disagree; Composer repaint fingerprint carries the
toggle so flipping it in Engine repaints an open Composer.

All wiring rides the existing sampler_inputs bridge — no backend change.
Verified live: knob visibility/badge, upsampler list fetch, Composer block
show/hide on toggle, typing sync, project state restored after test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 18:20:49 +03:00
digital-garbageandClaude Fable 5 a6e4a934f1 Segmented detailing: CLIPSeg-targeted latent crop-refine on Scene Chain
ADetailer-for-video probe core. After a scene denoises, CLIPSeg locates
user-named regions (hands, feet, ...) on decoded keyframes; the region is
cropped as a spatiotemporal tube, 2x-upsampled through Lightricks' trained
LatentUpsampler (comfy core, no port needed), re-denoised over the official
stage-2 tail (0.85/0.725/0.4219/0), downscaled back and pasted through the
feathered CLIPSeg silhouette. Final resolution unchanged; audio protected by
construction (refined audio discarded); tubes >35% frame refused; empty
targets/no-detection = bit-identical no-op. UNVALIDATED LIVE.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 18:10:10 +03:00
digital-garbageandClaude Sonnet 5 302028a491 Add rapid_start/rapid_end/rapid_start_end temporal styles
Punchy frame_rate mult held over the first/last slice of a scene's
denoise progress, easing to natural in the middle - same progress-
from-sigma mechanism as pulse, shaped as a single edge ramp instead
of repeated segments. Wired through build_enhancements (single-scene),
the Scene Chain Sampler's per-scene wrapper dispatch, and both Studio
and Editor temporal-style dropdowns.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 22:38:58 +03:00
digital-garbageandClaude Fable 5 80329c9f74 Loop roll v2: content-only roll — guides and audio memory stay pinned
Guide-carrying scenes no longer disable the loop temporal style. Appended
guide frames always live at the tail of the video stream's T range, and
keyframe_idxs is per-token — so the wrapper derives the tail frame count
(tokens / (H*W), same math as comfy's get_keyframe_idxs) and rolls only the
content region [0, T - tail); the tail, keyframe_idxs, and attention entries
that reference it stay canonical. The audio stream's appended memory (JoyAI,
rides a trailing mask=0 run) is detected the same way from audio_denoise_mask.
Guides keep informing every rolled position — their influence smears across
the cycle, which is what identity/style references should do in a loop — and
are never dragged into the rendered content.

Unparseable guide contexts (malformed keyframe_idxs, entries without idxs)
still run canonical: never risk a corrupting roll. Content-region size gates
the minimum-frames check now, so a short clip whose frames are mostly guide
tail stays canonical too.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 21:55:22 +03:00
digital-garbageandClaude Fable 5 a243b78e89 Release 3.3.0: CHANGELOG + version bump
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 21:29:35 +03:00
digital-garbageandClaude Fable 5 0214144a9d Loop temporal style: Mobius latent roll — seamless looping on LTX2.3
The 'loop' temporal style (in the style list since forever, but inert) now does
what WanVideoWrapper's Loop Args does for WAN: on eligible denoise steps the
packed latent is cyclically rolled along its temporal dim before the forward and
the prediction unrolled after, so the model repeatedly sees the video's seam
(last|first frame) as an interior frame pair at step-varying positions (van der
Corput sequence, reset per scene) and smooths it into a natural loop. Video and
audio streams roll by their own frame counts and stay time-aligned; denoise
masks roll in step, so i2v anchor per-token timesteps stay consistent.

Near-zero overhead: two clones + torch.roll per eligible step, no extra forward
passes. Rolls only below the near-noise plateau (nothing to smooth up there, and
the plateau step-cache stays valid). Calls carrying guides are left canonical —
keyframe_idxs pins appended guide frames to absolute positions inside the same
T range, so rolling them would corrupt; on guide-carrying scenes the roll is
inert and the run mechanism log says so.

Wiring: concrete style via the global build_enhancements path (innermost
relative to per-scene wrappers, as required — prediction-modifying wrappers see
canonical orientation only); auto director's funpack_temporal_loop via a tagged
per-scene wrapper on the Scene Chain sampler, installed below the guidance
stack. Editor already exposes 'loop' in the temporal style dropdown.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 21:20:04 +03:00
digital-garbageandClaude Fable 5 e15b08da60 Preview stability round 5: never serve raw moov-at-end video, window the player pool
/result: a failed faststart remux now answers 502 (rate-limited re-attempts)
instead of permanently marking the file serve-raw — raw moov-at-end bytes are
undecodable on seek in every browser (Firefox: NS_ERROR_DOM_MEDIA_DECODE_ERR /
AppleVTDecoder) and the element's cache-busted retries then fail identically
forever. Temp-dir failures 503 instead of falling back to raw; the loopback
fetch_view fallback spools ISO-BMFF video to temp and serves it through the
same remux + FileResponse path (Range support included); moov gates now cover
.m4v/.mov too.

player: materialize <video> elements only in a window around the playhead
(2 behind / 8 ahead, 16 max) instead of one per timeline clip. A montage-scale
timeline (100 clips) created 100 elements and 178 concurrent preview-segment
requests at once: Chrome hard-blocks media players past 75 per page, and the
per-origin connection pool starves on tunneled instances — clips in the back
half of the timeline never loaded while the front half played. Evicted and
reset elements release their src before detach (connection-pool class).

timeline: filmstrip probes run through a shared 2-at-a-time queue with a
20s wedge guard — painting a big timeline no longer starts one preload=auto
element per clip simultaneously.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 20:58:51 +03:00
digital-garbageandClaude Opus 4.8 bb4a87b4b5 Fix ALG crash on multi-scene: tensor 'or' in alg_latents (True,True) fallback
sample_funpack_distilled_flow built the alg_latents dict eagerly, and the
(True, True) value used 'anchor_blurred or tail_blurred' as its fallback when
both_blurred was None. When anchor blur is on but tail blur is off (or the tail
blur returned None), anchor_blurred is a multi-element tensor, so 'or' calls
bool() on it -> 'Boolean value of Tensor with more than one value is ambiguous'.

Surfaces only on continuation scenes: an anchorless first scene (identity/empty
latent, no denoise_mask) skips ALG entirely, but a later scene with a guide mask
enters ALG and trips the eager dict build. Replaced the tensor 'or' with explicit
None chaining (both -> anchor -> tail). Pure fallback; when (True,True) is actually
indexed both blurs are on, so both_blurred is non-None and the singles are never
reached. Independent of taste_nearest_prompt, which was merely on in the same run.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 20:47:18 +03:00
digital-garbageandClaude Opus 4.8 70f405b0e4 Per-prompt taste direction: retrieve nearest-prompt liked direction for embed/slider steering
Adds taste_nearest_prompt on the Scene Chain sampler. On every liked rating the
Refiner now records a (pooled-conditioning fingerprint -> liked direction) entry in a
ring buffer beside the global liked_dir. When the toggle is on, each scene retrieves
the similarity-weighted direction of its nearest rated prompts and uses that for
embed_guidance / score_slider instead of the one global average, falling back to the
global direction when nothing is close enough. Non-parametric (cosine lookup + vector
mean, no extra model forward, no value-function collapse risk). Wired into the Editor
Guidance panel. UNVALIDATED LIVE.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 20:19:27 +03:00
digital-garbageandClaude Opus 4.8 432e5c54bb Plateau step-cache: reuse near-noise plateau forward on Scene Chain sampler
MixCache/Chorus-family step-caching adapted to LTX2.3's distilled schedule.
On the near-pure-noise plateau (sigma >= threshold, default 0.975) the
transformer output barely changes, so compute the base-model forward once
and reuse it for the remaining plateau steps, skipping ~3-4 of 8 full
48-block forwards. Below threshold every step computes for real and the
cache drops, so structure formation is never approximated. Deterministic
given seed -> safe in Batch Training, no rating-diversity impact.

Installed innermost as a per-scene model_function_wrapper (existing
tag/strip leak hygiene) so guidance wrappers still post-process each step's
cached-or-fresh prediction. Cache keyed by (input.shape, cond_or_uncond).

Editor: plateau_cache + plateau_cache_threshold in Engine -> Experimental,
auto-wired via the sampler_inputs bridge (no backend/builder change).

Off by default, UNVALIDATED LIVE. (Chorus cross-variant reuse rejected --
would blur batch-rating diversity; FreeSpec dropped -- wrong topology.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 19:37:06 +03:00
digital-garbageandClaude Fable 5 f75a28982b Lock widget fields fed by an incoming wire; show their source
Wiring one node's output into another node's WIDGET input (e.g. Image
Transform's width/height → Empty Latent's width/height) works at generation —
the builder replaces the widget value with the connection — but the widget
field on the destination's node page stayed fully editable with no hint that
its value is ignored while wired.

Now a widget input with an incoming wire renders like a linked control: field
disabled, '⇐ <SourceNode> · <output>' tag (with an explanatory tooltip), eye
button hidden since an exposed control would edit a value generation ignores.
An ALREADY-exposed such widget in the main editor's Exposed controls block
renders the same wire-source note instead of an editable field (mirrors the
'← Project FPS' editor-link pattern). Unwiring restores the editable field and
eye button — the lock is computed from the wires on every render.

Verified live: GetImageSize width/height wired into EmptyLatentImage's
width/height widgets — both fields disabled with '⇐ GetImageSize · width/
height' tags on the node page, batch_size untouched, and the exposed width
control in the inspector shows the wire note instead of a number input.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 10:00:03 +03:00
digital-garbageandClaude Fable 5 c81ccdaf41 Fix exposed controls silently reverting: autosave snapshot taken before models sync
Reported as 'exposed Bypass toggle says disabled but the node's own checkbox
is checked (i2v still bypassed)'. Root cause in Store.commit(): the project
snapshot was deep-copied BEFORE _syncEditorStateToProject() ran, and only
scene_renders/scene_ghosts were backfilled afterwards — snapshot.models kept
the models config from the PREVIOUS sync. So after any exposed-control edit
(bypass, node input, or link — all saved via the /models endpoint), the next
project autosave uploaded the stale models copy inside the project body and
overwrote p.models on the server. The exposed control kept showing the local
value, the Models window fetched the server truth, and generation used the
server value — the exposed edit never took effect.

Fix: sync editor state into the project BEFORE taking the snapshot (the
backfill lines become redundant and are gone). Verified live against local
ComfyUI both ways: uncheck the exposed bypass → scene edit → autosave →
server models stay bypassed:false; toggle back on via the Models window's
save+event path → another autosave → server stays bypassed:true. 160
movie_editor tests pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 09:52:49 +03:00
digital-garbageandClaude Fable 5 dcfc610684 Player readiness slates: 'Video is processing/loading' instead of black/blinking preview
A clip whose render isn't decodable yet used to look broken — a black or
blinking <video> with console decode errors — with no signal about WHY or
whether it would recover. Now the player tracks per-element readiness
(loading → ready on loadedmetadata; downgraded on media reset) and the
full-cover slate says what's happening whenever the active clip's element
isn't ready:

- 'Video is processing…' — the server answered 503 on a HEAD probe (render
  still being written to disk, round-4 gate). These retry on a steady 3s
  cadence with the retry budget refunded, so a long GIL-bound save can no
  longer exhaust the 4 retries and leave the clip dead.
- 'Video is loading…' — bytes on the way / cache-busted retry in flight.
- 'Preview unavailable' — retry budget exhausted (re-armed on the next
  timeline rebuild, as before).

HEAD /result now mirrors the GET path's no-moov 503 so the probe can tell
'still being written' apart from a dead file (it used to claim 200 for a
file GET would refuse). Playback was already gated on loadedmetadata via
_playPending, so the preview only goes live once the video really decodes —
the slate makes that state visible instead of black.

Verified live against local ComfyUI with a real truncated mid-write mp4:
slate shows 'Video is processing…' while the file has no moov, HEAD answers
503, and within one 3s retry of the file completing on disk the slate clears
and the clip decodes and plays (readyState 4, zero media errors in console).
160 movie_editor tests pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 09:33:49 +03:00
digital-garbageandClaude Fable 5 9b202fec2d Preview stability round 4: never serve mid-write video, signature-keyed remux/segment caches
Three holes left after the /result faststart remux (e296fe0), matching the
still-live symptoms (decode errors in console, blinking clips, playback
restarting from 0, stale previews):

1. Mid-write serving: /result hit while VHS is still writing the mp4 found no
   moov, failed the remux, and served the raw moov-less bytes anyway —
   undecodable, and the player's cache-busted retries kept blinking against it.
2. Byte-switching under one URL: once the write finished, the same URL switched
   from raw to remuxed bytes with different offsets/length. A browser mid-Range
   stream mixes offsets (PIPELINE_ERROR_DECODE) or fails If-Range revalidation
   and falls back to a full 200 — resetting the element and restarting the clip
   from the beginning.
3. Cache collisions/staleness: the remux cache was keyed by path only (a
   rewritten source kept serving the old remux) and the remux output was named
   by basename only (two sources sharing a basename overwrote each other's
   remuxed copy with -y, possibly while it was being streamed).

Fix: _is_faststart becomes _moov_position (front/end/none) so "remux me"
(moov at end = complete VHS file) is distinguished from "come back later"
(no moov = still being written or aborted save). No-moov sources now get 503 +
Retry-After from both /result and preview-segment instead of undecodable bytes;
the player retry loop already handles that. The remux cache is keyed on the
source's (mtime_ns, size) signature, the output name is derived from
path+signature (no basename collisions), the remuxed file is validated
(nonzero, moov at front) before caching, and a stable-but-unremuxable file is
remembered so ffmpeg isn't re-run per request. The preview-segment cache key
also gains the source signature so a re-rendered source at the same path can't
serve segments encoded from the old bytes.

Verified against real ffmpeg files: end/front/none discrimination on complete,
faststart, truncated-mid-write, and empty mp4s; remux of a truncated file
raises instead of caching garbage. All 160 movie_editor tests pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 09:19:01 +03:00
digital-garbageandClaude Sonnet 5 4f121ff2f5 Fix undecodable scene previews: faststart-remux raw ComfyUI renders in /result
ComfyUI's VHS_VideoCombine node writes moov at the end of the mp4 (no
+faststart). Deep-seeking that file is exactly what preview-segment already
routes multi-scene chain clips around, but a "shallow" single clip (inSec
<=0.5) or just opening a scene's own preview hits /result directly, gets
served the raw moov-at-end file, and fails to decode the moment the user
scrubs anywhere into it — reproduced live as repeated
NS_ERROR_DOM_MEDIA_DECODE_ERR/AppleVTDecoder failures on the same file even
after cache-busted retries, since the bytes on disk are permanently bad for
seeking, not transiently mid-write.

Fix at the source instead of extending the segment path: /result now
stream-copy remuxes (+faststart, no re-encode) any .mp4 it serves that
doesn't already have moov up front, caching the remuxed copy so it only
happens once per file. FunPack's own segment/export/final-render output
already sets +faststart, so this is a no-op for everything except the raw
VHS output — verified the atom-order detection and remux against real
ffmpeg-produced faststart/non-faststart files.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-08 06:24:16 +03:00
digital-garbage 449532358f Fix stale FakeModel/FakeVAE mocks and expectations in test_scene_chain_sampler.py
model=object() lacked model_options (needed by the per-scene wrapper
snapshot/restore added for the scene-wrapper-leak fix), and FakeVAE lacked
decode() (needed since the sampler always decodes IMAGES when the caller
doesn't declare that output as disconnected) — together these silently
killed every test in the file that calls sample(). Added FakeModel and
FakeVAE.decode, then fixed the resulting return-tuple arity (sample() now
returns 6 values, these tests unpacked 4) and three genuinely stale
assertions: the per-scene report line gained a timing segment, carry_i2v_guides
now prepends its guide frame instead of appending it, and _append_i2v_guides
no longer routes through keyframe_idxs/guide_attention_entries.

Verified in isolation: all 14 tests in this file now pass. Running the full
tests/ suite still shows the same file failing — that's a separate,
pre-existing cross-file sys.modules stubbing collision (confirmed via
git stash on a clean dev checkout) affecting this file plus
test_mask_safe_attention.py and test_v2_category_alignment.py; not fixed
here since it isn't a one-line root cause.
2026-07-08 06:09:41 +03:00
digital-garbage 0c46f1f02e Carry frame overlap through anchor changes; fix identity_pin on anchor scenes
carry_overlap_through_anchor lets a scene keep the previous scene's latent
tail for background/environment continuity even when it switches to its own
i2v anchor, instead of the current hard cut. The anchor's own leading frame
stays untouched; only the frames after it carry the old tail.

Also fixes a real gap found along the way: the mixed-anchor branch never
called _apply_configured_guides, so Best-FaceID identity_transfer could
never resolve an identity_pin on an anchor-swap scene — exactly where it's
most useful. Added a standalone lookup so it resolves there too.
2026-07-08 05:57:39 +03:00
digital-garbageandClaude Sonnet 5 864d222ee5 Preview stability: route timeline filmstrip/waveform through segments
Today's preview-stability fix (4bc56e8) made the player pool fetch every
chain-shared clip through a trimmed, faststart preview segment instead of
deep-seeking the raw chain output (moov-at-end, stalls/blacks in the
browser). Two other consumers of the raw file were missed:

- appendFilmstrip built a <video preload=auto> pointed at the full shared
  chain output and seeked into it per thumbnail — the same deep-seek
  hazard the player fix eliminated, just in a different element.
- sceneAudioClip's waveform did a plain fetch()+arrayBuffer() of the
  ENTIRE shared file, once per scene sharing it, to decode a waveform.

Repro (fake 3-scene chain sharing one moov-at-end mp4): opening the
project fired 15 aborted requests fighting the player pool over Chrome's
6-per-origin cap — exactly the stale/black/reset-to-start symptoms
reported live. Both now request the same cached preview-segment URL the
player uses; post-fix, scrubbing 11/11 test positions succeeded with no
black frames or resets.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-06 22:50:09 +03:00
digital-garbageandClaude Sonnet 5 a4388d617f Best-FaceID identity transfer: full native overlap+ArcFace port
Replaces the source-phase-RoPE-only approximation with a faithful native port of
ComfyUI-BFSNodes' LTX Identity Transfer (read upstream source directly): the reference
face is now injected as separate, non-rendered overlap tokens (clean timestep, RoPE
source-phase tag, trimmed before output) instead of blending into Continuity's identity-pin
keyframe, plus an optional ArcFace embedding -> IdentityProjector -> context-token channel
(identity_projector, source_id, phase_scale, id_strength, arcface_mode, debug_log). Stays
on Chain Sampler per the Studio/Sampler boundary law's guide-ops carve-out. New
identity_transfer.py module; insightface added as a soft pip dependency.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-06 21:52:12 +03:00
digital-garbageandClaude Sonnet 5 05b628629b Release 3.2.1: CHANGELOG + version bump
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-06 20:59:44 +03:00
digital-garbageandClaude Sonnet 5 93dcf8dfc2 Setup-node picker collapses on select; autocomplete shows all matching shortcuts
Models & Pipeline -> +New node: picking a node now collapses the search results
into a one-line "selected" row with a Change button, instead of leaving the full
list open above the Values/Wiring panel.

Prompt autocomplete: dropped the MAX_ITEMS=8 cap on matchTriggers — a prefix
shared by many shortcuts (e.g. 15 "lazy" triggers) now shows all of them; the
menu already scrolls (max-height in CSS), so nothing was gained by truncating.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-06 20:55:04 +03:00
digital-garbageandClaude Sonnet 5 622e8ef747 Best-FaceID identity transfer: native source-phase RoPE port, no external node
Reverts the earlier ComfyUI-BFSNodes graph-splice approach (builder.py/timeline.py/
server.py scaffolding) in favor of porting the missing piece natively: FunPack's own
guide-attention path (LTXVAddGuide.add_keyframe_index + guide_attention_entries)
already places a reference image on the frame-0 grid exactly like BFSNodes' overlap
technique, and Continuity's existing "Identity pin" already applies one reference
face to every scene. The only real gap was the LoRA's trained-time source_phase RoPE
tag, added as a tag+strip patch on FunPackLTXAVSceneChainSampler scoped to the exact
guide-entry token range (never co-active mid_scene/carry/prior-scene guides sharing
the same keyframe_idxs tensor). New "Best-FaceID compatibility" + "Source-phase id"
knobs live under Engine Settings -> Experimental, alongside every other unvalidated
technique. LoRA loading itself reuses the existing generic Models-menu lora role.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-06 20:46:56 +03:00
digital-garbageandClaude Sonnet 5 79b0b5bb14 Best-FaceID identity transfer: one-checkbox LoRA + LTX Identity Transfer splice
Detects ComfyUI-BFSNodes' LTX Identity Transfer node in object_info and, when
enabled, auto-loads the Best-FaceID LoRA onto the model path and splices the
identity node between Studio and the Chain Sampler (project-wide, one
reference face per project — a run's graph can't vary it mid-chain). New
Engine settings pane under Chain Sampler picks the reference face (media
picker) and LoRA file (live dropdown), with advanced ArcFace/phase knobs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-06 20:08:26 +03:00
digital-garbageandClaude Fable 5 0df109480c Preview stability: async segment encodes, ghost segments, cache-keyed trims, preload budget
Four compounding defects behind stale/black previews on live instances:

1. /preview-segment ran its ffmpeg x264 re-encode synchronously inside the async
   handler — each encode froze ComfyUI's single aiohttp event loop for seconds,
   stalling every streaming /result response mid-transfer (elements error out,
   retries cache-bust and refetch, transport wedges). Worst right after a run,
   when the pool requests every clip's segment at once. Now asyncio.to_thread
   with a per-key lock (scrub storms don't spawn duplicate encodes) and
   -preset veryfast for preview trims. /export-clip wrapped the same way.

2. Ghosts were excluded from the segment system, so scrubbing a removed scene
   deep-seeked the raw moov-at-end chain MP4 — the exact stall segments were
   built to avoid. Ghosts (and lone clips with a deep in-point, e.g. the last
   surviving scene of a chain) now get segments; the trim window travels in the
   query (render_in + dur) since the scene no longer exists server-side.

3. Real staleness: segment URLs lacked the clip duration while responses are
   browser-cached for an hour — trimming a clip kept the same URL and replayed
   the hour-old segment. dur is now part of every segment URL.

4. Every pooled <video> used preload=auto: the whole timeline downloaded
   simultaneously, starving the browser's per-origin connections on tunneled
   instances and aborting the stream actually being watched. Pool now idles at
   preload=metadata; the current clip and its neighbours are promoted to auto.

Verified live on local ComfyUI with a synthetic 2-scene chain + ghost: ghost
segment 200/video-mp4, API stays responsive during an encode (17ms ping),
scrub across boundaries, seek into ghost (segment-relative time + badge),
play across the scene→ghost boundary, rapid pause/resume — all clean.
160 editor tests pass (new: ghost clip-spec parsing).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 10:34:13 +03:00
digital-garbageandClaude Fable 5 b8422d647b Guide blur: own strength + sigma-threshold controls, independent of ALG's
alg_blur_guides no longer borrows the sampler's anchor knobs: the Scene Chain
Sampler gains alg_guide_blur_strength (default 2.0) and
alg_guide_blur_sigma_threshold (default 0.975), threaded per-chunk via
extra_options like the tail count. The sampler now precomputes one latent per
(anchor blurred?, tail blurred?) combination — the frame sets are disjoint, so
the both-blurred variant composes the two passes — and each region follows its
own sigma window per step. Anchor ALG (alg_enabled/alg_strength/
alg_sigma_threshold) is untouched and back to anchor-only semantics.

Editor: 'Guide blur strength' and 'Guide blur sigma threshold' rows appear
under Engine ▸ Experimental when the blur toggle is on (sampler_inputs bridge,
names match the new node widgets; inputs appended at the block end to keep the
builder's positional widget mapping aligned).

Verified live: object_info shows both widgets appended last; Editor rows
appear on toggle and persist to project.sampler_inputs. 11 ALG unit tests
pass (new: composed independent-strength blur); same 20 pre-existing suite
failures.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 10:17:23 +03:00
digital-garbageandClaude Fable 5 6528acf504 Shortcut revolver: no-repeat cycling through multi-replacement shortcuts
Editor ▸ Settings ▸ Editor gains a 'Shortcut revolver' toggle (+ 'Random' sub-option).
When on, each shortcut with several replacements draws from a per-shortcut queue —
sequential (first…last, then restart) or shuffled once per cycle — and nothing repeats
until the whole set has fired. Off (default) keeps the seeded random pick unchanged.

- State is a sidecar (shortcut_revolver.json) next to the shortcut DB: settings +
  per-shortcut remaining queue + replacement-set fingerprint (editing a shortcut's
  replacements restarts its cycle; changing either setting resets all cycles).
- Generation commits the draw (refine_v2 positive prompt); every preview path
  (parse_timeline, split mapping, fragments) peeks the same stored queue without
  saving, so previews show exactly what the next Generate will use.
- Batch Training variants each commit a draw: N variants sweep N consecutive
  chambers instead of seed-rolled repeats.
- FunPackStudio.IS_CHANGED returns nan while the revolver is on so fixed-seed
  re-runs aren't served from ComfyUI's node cache (the next chamber must fire).

Verified live on local ComfyUI: settings round-trip, seed-independent stable peeks,
sequential cycle REP_ONE→TWO→THREE→ONE across processes, peek-after-commit shows the
next chamber. 9 new unit tests; full suite has the same 20 pre-existing failures.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 05:26:21 +03:00
digital-garbageandClaude Fable 5 1112c2dccc ALG blur guides: standalone — guide/JoyAI tail blur no longer requires alg_enabled
alg_blur_guides now works with the sampler's alg_enabled off: the anchor
stays sharp and only the newly-appended guide-attention frames (mid-scene
guide, carried guides, per-scene guides, JoyAI memory) are blurred, reusing
the same alg_strength / alg_sigma_threshold parameters and sigma window.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 05:11:33 +03:00
digital-garbageandClaude Fable 5 8eb6f3704a Ideas popover: habit-aware co-pilot for multi-shot prompt construction
The bulb's popover now mixes what the editor knows about YOUR prompting habits
with library browsing, in four sections:
- You've paired these before — shortcuts you historically put in the same shot
  as what's in the prompt, ranked by co-occurrence (reason chip: with «X» ×N)
- Your usual next shot — what usually follows the previous scene's shortcuts
  in timeline order (scene inspector only; reason chip: after «X» ×N)
- Continue the thread — more from categories the prompt already uses
- New directions — the existing missing-category browser (counts, +N more)

Habits are mined on demand by GET /api/library/suggestion_stats: a text scan of
all saved projects' scene texts (triggers stay verbatim in scene texts, so no
new persistent state — project files remain the only source of truth). Pairs =
same scene, follows = consecutive scenes in timeline order; editorial cut
subclips and excluded scenes are skipped. One-off co-occurrences (<2) ignored.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 04:04:04 +03:00
digital-garbageandClaude Fable 5 e163f4d006 Media gallery: one-click continuity pin on image cards
📌 hover action on image cards sets/unsets continuity_settings.identity_pin_ref
(same setting as Engine settings ▸ Continuity ▸ Identity pin, just quicker).
The pinned card shows a persistent 📌 badge on its thumbnail. Pinning while
Auto continuity is explicitly off re-enables it — a pin that does nothing
would be a hidden setting. Image cards only, chain-sampler pipelines only.

fpMediabrowser now fingerprints identity_pin_ref so pin changes repaint the
gallery zone (they were dropped by the ViewBus fingerprint before).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 03:52:40 +03:00