refactor: reuse director serialization helpers
This commit is contained in:
@@ -9,9 +9,52 @@ const MOTION_TRACK_HEIGHT = 80; // used as Motion Guide track height
|
|||||||
const CANVAS_HEIGHT = RULER_HEIGHT + BLOCK_HEIGHT + MOTION_TRACK_HEIGHT + AUDIO_TRACK_HEIGHT;
|
const CANVAS_HEIGHT = RULER_HEIGHT + BLOCK_HEIGHT + MOTION_TRACK_HEIGHT + AUDIO_TRACK_HEIGHT;
|
||||||
const HANDLE_HIT_PX = 14;
|
const HANDLE_HIT_PX = 14;
|
||||||
const MIN_SEGMENT_LENGTH = 6;
|
const MIN_SEGMENT_LENGTH = 6;
|
||||||
const MAX_THUMBNAIL_DIM = 512; // Increased to maintain quality for taller images
|
const MAX_THUMBNAIL_DIM = 512; // Increased to maintain quality for taller images
|
||||||
|
|
||||||
const HIDDEN_WIDGET_NAMES = ["timeline_data", "local_prompts", "segment_lengths", "guide_strength", "audio_data", "use_custom_audio", "inpaint_audio", "use_custom_motion", "override_audio"];
|
const HIDDEN_WIDGET_NAMES = ["timeline_data", "local_prompts", "segment_lengths", "guide_strength", "audio_data", "use_custom_audio", "inpaint_audio", "use_custom_motion", "override_audio"];
|
||||||
|
const DEFAULT_WIDGET_VALUES = {
|
||||||
|
inpaint_audio: true,
|
||||||
|
override_audio: false,
|
||||||
|
use_custom_audio: false,
|
||||||
|
use_custom_motion: true,
|
||||||
|
frame_rate: 24,
|
||||||
|
display_mode: "seconds",
|
||||||
|
custom_width: 0,
|
||||||
|
custom_height: 0,
|
||||||
|
resize_method: "maintain aspect ratio",
|
||||||
|
divisible_by: 32,
|
||||||
|
img_compression: 18,
|
||||||
|
guide_strength: "",
|
||||||
|
local_prompts: "",
|
||||||
|
segment_lengths: "",
|
||||||
|
timeline_data: "{}",
|
||||||
|
epsilon: 0.001,
|
||||||
|
start_second: 0.0,
|
||||||
|
end_second: 5.0,
|
||||||
|
duration_seconds: 5.0,
|
||||||
|
start_frame: 0,
|
||||||
|
end_frame: 120,
|
||||||
|
duration_frames: 120,
|
||||||
|
};
|
||||||
|
|
||||||
|
function stripImageSegmentTransient(s) {
|
||||||
|
const { imgObj, videoEl, _isSeeking, thumbnails, _extractingThumbs, _sSecs, _lSecs, _tSecs, _dSecs, _uploading, _blobUrl, ...rest } = s;
|
||||||
|
return rest;
|
||||||
|
}
|
||||||
|
|
||||||
|
function stripMotionSegmentTransient(s) {
|
||||||
|
const { videoEl, _isSeeking, thumbnails, _extractingThumbs, _sSecs, _lSecs, _tSecs, _dSecs, _uploading, _blobUrl, ...rest } = s;
|
||||||
|
return rest;
|
||||||
|
}
|
||||||
|
|
||||||
|
function stripAudioSegmentTransient(s) {
|
||||||
|
const { _sSecs, _lSecs, _tSecs, _dSecs, _uploading, _decoding, _blobUrl, _audioBuffer, ...rest } = s;
|
||||||
|
return rest;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dedupeById(items = []) {
|
||||||
|
return items.filter((seg, index, self) => index === self.findIndex((s) => s.id === seg.id));
|
||||||
|
}
|
||||||
|
|
||||||
function hideWidget(w) {
|
function hideWidget(w) {
|
||||||
if (!w) return;
|
if (!w) return;
|
||||||
@@ -749,25 +792,16 @@ function parseInitial(jsonStr) {
|
|||||||
while (parsed.characters.length < 3) {
|
while (parsed.characters.length < 3) {
|
||||||
parsed.characters.push({ images: [], description: "" });
|
parsed.characters.push({ images: [], description: "" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Array.isArray(p.segments)) {
|
if (Array.isArray(p.segments)) {
|
||||||
parsed.segments = p.segments.map(s => {
|
parsed.segments = p.segments.map(stripImageSegmentTransient);
|
||||||
const { imgObj, videoEl, _isSeeking, thumbnails, _extractingThumbs, _sSecs, _lSecs, _tSecs, _dSecs, _uploading, _blobUrl, ...rest } = s;
|
}
|
||||||
return rest;
|
if (Array.isArray(p.motionSegments)) {
|
||||||
});
|
parsed.motionSegments = p.motionSegments.map(stripMotionSegmentTransient);
|
||||||
}
|
}
|
||||||
if (Array.isArray(p.motionSegments)) {
|
if (Array.isArray(p.audioSegments)) {
|
||||||
parsed.motionSegments = p.motionSegments.map(s => {
|
parsed.audioSegments = p.audioSegments.map(stripAudioSegmentTransient);
|
||||||
const { videoEl, _isSeeking, thumbnails, _extractingThumbs, _sSecs, _lSecs, _tSecs, _dSecs, _uploading, _blobUrl, ...rest } = s;
|
}
|
||||||
return rest;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (Array.isArray(p.audioSegments)) {
|
|
||||||
parsed.audioSegments = p.audioSegments.map(s => {
|
|
||||||
const { _sSecs, _lSecs, _tSecs, _dSecs, _uploading, _decoding, ...rest } = s;
|
|
||||||
return rest;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (e) { }
|
} catch (e) { }
|
||||||
|
|
||||||
@@ -9345,16 +9379,16 @@ class TimelineEditor {
|
|||||||
input.addEventListener("blur", () => { setTimeout(hideMenu, 150); });
|
input.addEventListener("blur", () => { setTimeout(hideMenu, 150); });
|
||||||
}
|
}
|
||||||
|
|
||||||
commitChanges(skipRender = false) {
|
commitChanges(skipRender = false) {
|
||||||
if (this._suppressCommit) return;
|
if (this._suppressCommit) return;
|
||||||
// Deduplicate segments by ID to clean up any duplicates created by the previous onseeked bug
|
// Deduplicate segments by ID to clean up any duplicates created by the previous onseeked bug
|
||||||
this.timeline.segments = this.timeline.segments.filter((seg, index, self) => index === self.findIndex((s) => s.id === seg.id));
|
this.timeline.segments = dedupeById(this.timeline.segments);
|
||||||
if (this.timeline.audioSegments) {
|
if (this.timeline.audioSegments) {
|
||||||
this.timeline.audioSegments = this.timeline.audioSegments.filter((seg, index, self) => index === self.findIndex((s) => s.id === seg.id));
|
this.timeline.audioSegments = dedupeById(this.timeline.audioSegments);
|
||||||
}
|
}
|
||||||
if (this.timeline.motionSegments) {
|
if (this.timeline.motionSegments) {
|
||||||
this.timeline.motionSegments = this.timeline.motionSegments.filter((seg, index, self) => index === self.findIndex((s) => s.id === seg.id));
|
this.timeline.motionSegments = dedupeById(this.timeline.motionSegments);
|
||||||
}
|
}
|
||||||
|
|
||||||
let sortedSegments = [...this.timeline.segments].sort((a, b) => a.start - b.start);
|
let sortedSegments = [...this.timeline.segments].sort((a, b) => a.start - b.start);
|
||||||
let contiguousLengths = [];
|
let contiguousLengths = [];
|
||||||
@@ -9473,23 +9507,14 @@ class TimelineEditor {
|
|||||||
analyzeProvider: this.timeline.analyzeProvider || "ollama",
|
analyzeProvider: this.timeline.analyzeProvider || "ollama",
|
||||||
analyzeBaseUrl: this.timeline.analyzeBaseUrl || "",
|
analyzeBaseUrl: this.timeline.analyzeBaseUrl || "",
|
||||||
analyzeModel: this.timeline.analyzeModel || "",
|
analyzeModel: this.timeline.analyzeModel || "",
|
||||||
characters: (this.timeline.characters || []).map(c => ({
|
characters: (this.timeline.characters || []).map(c => ({
|
||||||
images: (c.images || []).map(img => img.b64 ? { b64: img.b64, name: img.name } : { name: img.name }),
|
images: (c.images || []).map(img => img.b64 ? { b64: img.b64, name: img.name } : { name: img.name }),
|
||||||
description: c.description || ""
|
description: c.description || ""
|
||||||
})),
|
})),
|
||||||
segments: sortedSegments.map(s => {
|
segments: sortedSegments.map(stripImageSegmentTransient),
|
||||||
const { imgObj, videoEl, _isSeeking, thumbnails, _extractingThumbs, _sSecs, _lSecs, _tSecs, _dSecs, _uploading, _blobUrl, ...rest } = s;
|
motionSegments: (this.timeline.motionSegments || []).map(stripMotionSegmentTransient),
|
||||||
return rest;
|
audioSegments: (this.timeline.audioSegments || []).map(stripAudioSegmentTransient)
|
||||||
}),
|
};
|
||||||
motionSegments: (this.timeline.motionSegments || []).map(s => {
|
|
||||||
const { imgObj, videoEl, _isSeeking, thumbnails, _extractingThumbs, _sSecs, _lSecs, _tSecs, _dSecs, _uploading, _blobUrl, ...rest } = s;
|
|
||||||
return rest;
|
|
||||||
}),
|
|
||||||
audioSegments: (this.timeline.audioSegments || []).map(s => {
|
|
||||||
const { _sSecs, _lSecs, _tSecs, _dSecs, _uploading, _decoding, _blobUrl, _audioBuffer, ...rest } = s;
|
|
||||||
return rest;
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
const jsonStr = JSON.stringify(toSave);
|
const jsonStr = JSON.stringify(toSave);
|
||||||
console.log("[LTXDirector debug] commitChanges: saving timelineDataWidget value:", jsonStr);
|
console.log("[LTXDirector debug] commitChanges: saving timelineDataWidget value:", jsonStr);
|
||||||
@@ -10881,29 +10906,20 @@ class TimelineEditor {
|
|||||||
retakeLength: this.timeline.retakeLength,
|
retakeLength: this.timeline.retakeLength,
|
||||||
retakePrompt: this.timeline.retakePrompt,
|
retakePrompt: this.timeline.retakePrompt,
|
||||||
retakeStrength: this.timeline.retakeStrength,
|
retakeStrength: this.timeline.retakeStrength,
|
||||||
retakeVideo: this.timeline.retakeVideo ? {
|
retakeVideo: this.timeline.retakeVideo ? {
|
||||||
fileName: this.timeline.retakeVideo.fileName,
|
fileName: this.timeline.retakeVideo.fileName,
|
||||||
imageFile: this.timeline.retakeVideo.imageFile,
|
imageFile: this.timeline.retakeVideo.imageFile,
|
||||||
videoDurationFrames: this.timeline.retakeVideo.videoDurationFrames,
|
videoDurationFrames: this.timeline.retakeVideo.videoDurationFrames,
|
||||||
fileSize: this.timeline.retakeVideo.fileSize,
|
fileSize: this.timeline.retakeVideo.fileSize,
|
||||||
} : null,
|
} : null,
|
||||||
normalStartFrame: this.timeline.normalStartFrame,
|
normalStartFrame: this.timeline.normalStartFrame,
|
||||||
normalDurationFrames: this.timeline.normalDurationFrames,
|
normalDurationFrames: this.timeline.normalDurationFrames,
|
||||||
segments: (this.timeline.segments || []).map(s => {
|
segments: (this.timeline.segments || []).map(stripImageSegmentTransient),
|
||||||
const { imgObj, videoEl, _isSeeking, thumbnails, _extractingThumbs, _sSecs, _lSecs, _tSecs, _dSecs, _uploading, _blobUrl, ...rest } = s;
|
motionSegments: (this.timeline.motionSegments || []).map(stripMotionSegmentTransient),
|
||||||
return rest;
|
audioSegments: (this.timeline.audioSegments || []).map(stripAudioSegmentTransient)
|
||||||
}),
|
}
|
||||||
motionSegments: (this.timeline.motionSegments || []).map(s => {
|
}, null, 2);
|
||||||
const { imgObj, videoEl, _isSeeking, thumbnails, _extractingThumbs, _sSecs, _lSecs, _tSecs, _dSecs, _uploading, _blobUrl, ...rest } = s;
|
}
|
||||||
return rest;
|
|
||||||
}),
|
|
||||||
audioSegments: (this.timeline.audioSegments || []).map(s => {
|
|
||||||
const { _sSecs, _lSecs, _tSecs, _dSecs, _uploading, _decoding, _blobUrl, _audioBuffer, ...rest } = s;
|
|
||||||
return rest;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}, null, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
async handleSaveTimeline() {
|
async handleSaveTimeline() {
|
||||||
if (!this.currentFileHandle) {
|
if (!this.currentFileHandle) {
|
||||||
@@ -12260,32 +12276,7 @@ app.registerExtension({
|
|||||||
"resize_method", "divisible_by", "img_compression", "override_audio", "timeline_ui"
|
"resize_method", "divisible_by", "img_compression", "override_audio", "timeline_ui"
|
||||||
];
|
];
|
||||||
|
|
||||||
const ALL_WIDGET_DEFAULTS = {
|
let names = SCHEMA_23;
|
||||||
inpaint_audio: true,
|
|
||||||
override_audio: false,
|
|
||||||
use_custom_audio: false,
|
|
||||||
use_custom_motion: true,
|
|
||||||
frame_rate: 24,
|
|
||||||
display_mode: "seconds",
|
|
||||||
custom_width: 0,
|
|
||||||
custom_height: 0,
|
|
||||||
resize_method: "maintain aspect ratio",
|
|
||||||
divisible_by: 32,
|
|
||||||
img_compression: 18,
|
|
||||||
guide_strength: "",
|
|
||||||
local_prompts: "",
|
|
||||||
segment_lengths: "",
|
|
||||||
timeline_data: "{}",
|
|
||||||
epsilon: 0.001,
|
|
||||||
start_second: 0.0,
|
|
||||||
end_second: 5.0,
|
|
||||||
duration_seconds: 5.0,
|
|
||||||
start_frame: 0,
|
|
||||||
end_frame: 120,
|
|
||||||
duration_frames: 120,
|
|
||||||
};
|
|
||||||
|
|
||||||
let names = SCHEMA_23;
|
|
||||||
const len = info.widgets_values.length;
|
const len = info.widgets_values.length;
|
||||||
if (len <= 19) {
|
if (len <= 19) {
|
||||||
names = SCHEMA_19;
|
names = SCHEMA_19;
|
||||||
@@ -12301,14 +12292,14 @@ app.registerExtension({
|
|||||||
|
|
||||||
if (this.widgets) {
|
if (this.widgets) {
|
||||||
for (const w of this.widgets) {
|
for (const w of this.widgets) {
|
||||||
const schemaIdx = names.indexOf(w.name);
|
const schemaIdx = names.indexOf(w.name);
|
||||||
if (schemaIdx !== -1 && schemaIdx < len) {
|
if (schemaIdx !== -1 && schemaIdx < len) {
|
||||||
setWidgetValue(w, info.widgets_values[schemaIdx]);
|
setWidgetValue(w, info.widgets_values[schemaIdx]);
|
||||||
} else if (ALL_WIDGET_DEFAULTS.hasOwnProperty(w.name)) {
|
} else if (DEFAULT_WIDGET_VALUES.hasOwnProperty(w.name)) {
|
||||||
setWidgetValue(w, ALL_WIDGET_DEFAULTS[w.name]);
|
setWidgetValue(w, DEFAULT_WIDGET_VALUES[w.name]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populate properties with these restored values
|
// Populate properties with these restored values
|
||||||
if (this.widgets) {
|
if (this.widgets) {
|
||||||
|
|||||||
Reference in New Issue
Block a user