perf: tighten director tag and segment helpers
This commit is contained in:
@@ -53,7 +53,15 @@ function stripAudioSegmentTransient(s) {
|
||||
}
|
||||
|
||||
function dedupeById(items = []) {
|
||||
return items.filter((seg, index, self) => index === self.findIndex((s) => s.id === seg.id));
|
||||
const seen = new Set();
|
||||
const deduped = [];
|
||||
for (const seg of items || []) {
|
||||
const id = seg?.id;
|
||||
if (!id || seen.has(id)) continue;
|
||||
seen.add(id);
|
||||
deduped.push(seg);
|
||||
}
|
||||
return deduped;
|
||||
}
|
||||
|
||||
function findActiveSegmentAtFrame(segments = [], targetFrame, type) {
|
||||
@@ -61,6 +69,10 @@ function findActiveSegmentAtFrame(segments = [], targetFrame, type) {
|
||||
(seg) => seg?.type === type && targetFrame >= seg.start && targetFrame < seg.start + seg.length
|
||||
);
|
||||
}
|
||||
|
||||
function segmentMatchesAudioSource(seg, audioFile, blobUrl) {
|
||||
return !!seg && (seg.audioFile === audioFile || seg._blobUrl === blobUrl);
|
||||
}
|
||||
|
||||
function hideWidget(w) {
|
||||
if (!w) return;
|
||||
@@ -2004,6 +2016,14 @@ class TimelineEditor {
|
||||
return found;
|
||||
}
|
||||
|
||||
_forEachMatchingAudioSegment(audioFile, blobUrl, visitor) {
|
||||
for (const seg of this.timeline.audioSegments || []) {
|
||||
if (segmentMatchesAudioSource(seg, audioFile, blobUrl)) {
|
||||
visitor(seg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_getSegmentsByFileKey(fileKey) {
|
||||
const matches = [];
|
||||
this._forEachMatchingSegment(fileKey, (seg) => {
|
||||
@@ -2231,11 +2251,10 @@ class TimelineEditor {
|
||||
|
||||
const audioBuffer = await this._loadAudioBuffer(seg);
|
||||
|
||||
const matchingSegs = this.timeline.audioSegments.filter(s => s.audioFile === seg.audioFile || s._blobUrl === seg._blobUrl);
|
||||
for (const s of matchingSegs) {
|
||||
s._audioBuffer = audioBuffer;
|
||||
s._decoding = false;
|
||||
}
|
||||
this._forEachMatchingAudioSegment(seg.audioFile, seg._blobUrl, (s) => {
|
||||
s._audioBuffer = audioBuffer;
|
||||
s._decoding = false;
|
||||
});
|
||||
} catch (err) {
|
||||
console.warn("Failed to preload audio segment:", err);
|
||||
seg._decoding = false;
|
||||
|
||||
@@ -32,7 +32,12 @@ from .prompt_relay import (
|
||||
|
||||
from .patches import detect_model_type, apply_patches
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log = logging.getLogger(__name__)
|
||||
CHARACTER_TAG_GROUPS = (
|
||||
("@character1", "@char1"),
|
||||
("@character2", "@char2"),
|
||||
("@character3", "@char3"),
|
||||
)
|
||||
|
||||
# Setup global event loop exception handler to silence ConnectionResetError (WinError 10054/10053) on Windows
|
||||
try:
|
||||
@@ -281,40 +286,26 @@ def _time_range_to_latent_indices(rel_start_frames: float, rel_length_frames: fl
|
||||
return start_idx, end_idx
|
||||
|
||||
|
||||
def _preprocess_prompts_with_characters(global_prompt, local_prompts, char1="", char2="", char3=""):
|
||||
"""Invisibly swaps out @character1/@char1 tags with their high-fidelity VLM descriptions."""
|
||||
gp = global_prompt or ""
|
||||
char1 = char1 if char1 else ""
|
||||
char2 = char2 if char2 else ""
|
||||
char3 = char3 if char3 else ""
|
||||
|
||||
# Process Global Prompt
|
||||
for tag in ["@character1", "@char1"]:
|
||||
if tag in gp:
|
||||
gp = gp.replace(tag, char1)
|
||||
for tag in ["@character2", "@char2"]:
|
||||
if tag in gp:
|
||||
gp = gp.replace(tag, char2)
|
||||
for tag in ["@character3", "@char3"]:
|
||||
if tag in gp:
|
||||
gp = gp.replace(tag, char3)
|
||||
|
||||
# Process Local Timeline Prompts
|
||||
locals_list = [p.strip() for p in local_prompts.split("|")] if local_prompts else []
|
||||
processed_locals = []
|
||||
for lp in locals_list:
|
||||
for tag in ["@character1", "@char1"]:
|
||||
if tag in lp:
|
||||
lp = lp.replace(tag, char1)
|
||||
for tag in ["@character2", "@char2"]:
|
||||
if tag in lp:
|
||||
lp = lp.replace(tag, char2)
|
||||
for tag in ["@character3", "@char3"]:
|
||||
if tag in lp:
|
||||
lp = lp.replace(tag, char3)
|
||||
processed_locals.append(lp)
|
||||
|
||||
return gp, " | ".join(processed_locals)
|
||||
def _preprocess_prompts_with_characters(global_prompt, local_prompts, char1="", char2="", char3=""):
|
||||
"""Invisibly swaps out @character1/@char1 tags with their high-fidelity VLM descriptions."""
|
||||
replacements = {}
|
||||
for tags, value in zip(CHARACTER_TAG_GROUPS, (char1 or "", char2 or "", char3 or "")):
|
||||
for tag in tags:
|
||||
replacements[tag] = value
|
||||
|
||||
def apply_replacements(text: str) -> str:
|
||||
updated = text or ""
|
||||
for tag, replacement in replacements.items():
|
||||
if tag in updated:
|
||||
updated = updated.replace(tag, replacement)
|
||||
return updated
|
||||
|
||||
gp = apply_replacements(global_prompt or "")
|
||||
if not local_prompts:
|
||||
return gp, ""
|
||||
|
||||
processed_locals = [apply_replacements(part.strip()) for part in local_prompts.split("|")]
|
||||
return gp, " | ".join(processed_locals)
|
||||
|
||||
|
||||
def _load_image_source(b64_or_url: str, filename: str = None, cache: dict | None = None,
|
||||
@@ -1391,8 +1382,7 @@ def _build_reference_mode_outputs(
|
||||
raise ValueError("Licon MSR (Prefix) ref option requires connecting the VAE to LTX Director!")
|
||||
|
||||
prompt_text = (global_prompt or "") + " " + (local_prompts or "")
|
||||
tag_pairs = [("@character1", "@char1"), ("@character2", "@char2"), ("@character3", "@char3")]
|
||||
referenced_slots = [i for i, tags in enumerate(tag_pairs) if any(t in prompt_text for t in tags)]
|
||||
referenced_slots = [i for i, tags in enumerate(CHARACTER_TAG_GROUPS) if any(t in prompt_text for t in tags)]
|
||||
selected = []
|
||||
for slot in referenced_slots:
|
||||
if slot < len(char_slot_images):
|
||||
|
||||
Reference in New Issue
Block a user