Fix 7 bugs: bounds checks, deepcopy, time import, JS keys, unused import
- Add bounds check on src_batch index in add_from_source and copy_source
- Guard delete callback against stale index after rapid clicks
- Replace __import__('time').time() with time.time() in sync_to_db
- Use deepcopy(DEFAULTS) consistently in utils.py and main.py
- Use JSON.stringify in JS onConfigure fallback path for key storage
- Read state.show_comfy_monitor for checkbox initial value
- Remove unused KEY_BATCH_DATA import from tab_projects_ng
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
5
main.py
5
main.py
@@ -1,3 +1,4 @@
|
|||||||
|
import copy
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -481,7 +482,7 @@ def render_sidebar(state: AppState, dual_pane: dict):
|
|||||||
if not fn.endswith('.json'):
|
if not fn.endswith('.json'):
|
||||||
fn += '.json'
|
fn += '.json'
|
||||||
path = state.current_dir / fn
|
path = state.current_dir / fn
|
||||||
first_item = DEFAULTS.copy()
|
first_item = copy.deepcopy(DEFAULTS)
|
||||||
first_item[KEY_SEQUENCE_NUMBER] = 1
|
first_item[KEY_SEQUENCE_NUMBER] = 1
|
||||||
save_json(path, {KEY_BATCH_DATA: [first_item]})
|
save_json(path, {KEY_BATCH_DATA: [first_item]})
|
||||||
new_fn_input.set_value('')
|
new_fn_input.set_value('')
|
||||||
@@ -514,7 +515,7 @@ def render_sidebar(state: AppState, dual_pane: dict):
|
|||||||
state.show_comfy_monitor = e.value
|
state.show_comfy_monitor = e.value
|
||||||
state._render_main.refresh()
|
state._render_main.refresh()
|
||||||
|
|
||||||
ui.checkbox('Show Comfy Monitor', value=True, on_change=on_monitor_toggle)
|
ui.checkbox('Show Comfy Monitor', value=state.show_comfy_monitor, on_change=on_monitor_toggle)
|
||||||
|
|
||||||
|
|
||||||
# Register REST API routes for ComfyUI connectivity (uses the shared DB instance)
|
# Register REST API routes for ComfyUI connectivity (uses the shared DB instance)
|
||||||
|
|||||||
@@ -273,7 +273,7 @@ def render_batch_processor(state: AppState):
|
|||||||
item = copy.deepcopy(DEFAULTS)
|
item = copy.deepcopy(DEFAULTS)
|
||||||
src_batch = _src_cache['batch']
|
src_batch = _src_cache['batch']
|
||||||
sel_idx = src_seq_select.value
|
sel_idx = src_seq_select.value
|
||||||
if src_batch and sel_idx is not None:
|
if src_batch and sel_idx is not None and int(sel_idx) < len(src_batch):
|
||||||
item.update(copy.deepcopy(src_batch[int(sel_idx)]))
|
item.update(copy.deepcopy(src_batch[int(sel_idx)]))
|
||||||
elif _src_cache['data']:
|
elif _src_cache['data']:
|
||||||
item.update(copy.deepcopy(_src_cache['data']))
|
item.update(copy.deepcopy(_src_cache['data']))
|
||||||
@@ -398,7 +398,7 @@ def _render_sequence_card(i, seq, batch_list, data, file_path, state,
|
|||||||
item = copy.deepcopy(DEFAULTS)
|
item = copy.deepcopy(DEFAULTS)
|
||||||
src_batch = src_cache['batch']
|
src_batch = src_cache['batch']
|
||||||
sel_idx = src_seq_select.value
|
sel_idx = src_seq_select.value
|
||||||
if src_batch and sel_idx is not None:
|
if src_batch and sel_idx is not None and int(sel_idx) < len(src_batch):
|
||||||
item.update(copy.deepcopy(src_batch[int(sel_idx)]))
|
item.update(copy.deepcopy(src_batch[int(sel_idx)]))
|
||||||
elif src_cache['data']:
|
elif src_cache['data']:
|
||||||
item.update(copy.deepcopy(src_cache['data']))
|
item.update(copy.deepcopy(src_cache['data']))
|
||||||
@@ -453,8 +453,9 @@ def _render_sequence_card(i, seq, batch_list, data, file_path, state,
|
|||||||
|
|
||||||
# Delete
|
# Delete
|
||||||
def delete(idx=i):
|
def delete(idx=i):
|
||||||
batch_list.pop(idx)
|
if idx < len(batch_list):
|
||||||
commit()
|
batch_list.pop(idx)
|
||||||
|
commit()
|
||||||
|
|
||||||
ui.button(icon='delete', on_click=delete).props('color=negative')
|
ui.button(icon='delete', on_click=delete).props('color=negative')
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from nicegui import ui
|
|||||||
|
|
||||||
from state import AppState
|
from state import AppState
|
||||||
from db import ProjectDB
|
from db import ProjectDB
|
||||||
from utils import save_config, sync_to_db, KEY_BATCH_DATA
|
from utils import save_config, sync_to_db
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|||||||
13
utils.py
13
utils.py
@@ -1,3 +1,4 @@
|
|||||||
|
import copy
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
@@ -182,7 +183,7 @@ def sync_to_db(db, project_name: str, file_path: Path, data: dict) -> None:
|
|||||||
top_level = {k: v for k, v in data.items()
|
top_level = {k: v for k, v in data.items()
|
||||||
if k not in (KEY_BATCH_DATA, KEY_HISTORY_TREE)}
|
if k not in (KEY_BATCH_DATA, KEY_HISTORY_TREE)}
|
||||||
if not df:
|
if not df:
|
||||||
now = __import__('time').time()
|
now = time.time()
|
||||||
cur = db.conn.execute(
|
cur = db.conn.execute(
|
||||||
"INSERT INTO data_files (project_id, name, data_type, top_level, created_at, updated_at) "
|
"INSERT INTO data_files (project_id, name, data_type, top_level, created_at, updated_at) "
|
||||||
"VALUES (?, ?, ?, ?, ?, ?)",
|
"VALUES (?, ?, ?, ?, ?, ?)",
|
||||||
@@ -192,7 +193,7 @@ def sync_to_db(db, project_name: str, file_path: Path, data: dict) -> None:
|
|||||||
else:
|
else:
|
||||||
df_id = df["id"]
|
df_id = df["id"]
|
||||||
# Update top_level metadata
|
# Update top_level metadata
|
||||||
now = __import__('time').time()
|
now = time.time()
|
||||||
db.conn.execute(
|
db.conn.execute(
|
||||||
"UPDATE data_files SET top_level = ?, updated_at = ? WHERE id = ?",
|
"UPDATE data_files SET top_level = ?, updated_at = ? WHERE id = ?",
|
||||||
(json.dumps(top_level), now, df_id),
|
(json.dumps(top_level), now, df_id),
|
||||||
@@ -206,7 +207,7 @@ def sync_to_db(db, project_name: str, file_path: Path, data: dict) -> None:
|
|||||||
if not isinstance(item, dict):
|
if not isinstance(item, dict):
|
||||||
continue
|
continue
|
||||||
seq_num = int(item.get(KEY_SEQUENCE_NUMBER, 0))
|
seq_num = int(item.get(KEY_SEQUENCE_NUMBER, 0))
|
||||||
now = __import__('time').time()
|
now = time.time()
|
||||||
db.conn.execute(
|
db.conn.execute(
|
||||||
"INSERT INTO sequences (data_file_id, sequence_number, data, updated_at) "
|
"INSERT INTO sequences (data_file_id, sequence_number, data, updated_at) "
|
||||||
"VALUES (?, ?, ?, ?)",
|
"VALUES (?, ?, ?, ?)",
|
||||||
@@ -216,7 +217,7 @@ def sync_to_db(db, project_name: str, file_path: Path, data: dict) -> None:
|
|||||||
# Sync history tree
|
# Sync history tree
|
||||||
history_tree = data.get(KEY_HISTORY_TREE)
|
history_tree = data.get(KEY_HISTORY_TREE)
|
||||||
if history_tree and isinstance(history_tree, dict):
|
if history_tree and isinstance(history_tree, dict):
|
||||||
now = __import__('time').time()
|
now = time.time()
|
||||||
db.conn.execute(
|
db.conn.execute(
|
||||||
"INSERT INTO history_trees (data_file_id, tree_data, updated_at) "
|
"INSERT INTO history_trees (data_file_id, tree_data, updated_at) "
|
||||||
"VALUES (?, ?, ?) "
|
"VALUES (?, ?, ?) "
|
||||||
@@ -237,10 +238,10 @@ def sync_to_db(db, project_name: str, file_path: Path, data: dict) -> None:
|
|||||||
|
|
||||||
def generate_templates(current_dir: Path) -> None:
|
def generate_templates(current_dir: Path) -> None:
|
||||||
"""Creates batch template files if folder is empty."""
|
"""Creates batch template files if folder is empty."""
|
||||||
first = DEFAULTS.copy()
|
first = copy.deepcopy(DEFAULTS)
|
||||||
first[KEY_SEQUENCE_NUMBER] = 1
|
first[KEY_SEQUENCE_NUMBER] = 1
|
||||||
save_json(current_dir / "batch_prompt_i2v.json", {KEY_BATCH_DATA: [first]})
|
save_json(current_dir / "batch_prompt_i2v.json", {KEY_BATCH_DATA: [first]})
|
||||||
|
|
||||||
first2 = DEFAULTS.copy()
|
first2 = copy.deepcopy(DEFAULTS)
|
||||||
first2[KEY_SEQUENCE_NUMBER] = 1
|
first2[KEY_SEQUENCE_NUMBER] = 1
|
||||||
save_json(current_dir / "batch_prompt_vace_extend.json", {KEY_BATCH_DATA: [first2]})
|
save_json(current_dir / "batch_prompt_vace_extend.json", {KEY_BATCH_DATA: [first2]})
|
||||||
|
|||||||
@@ -251,8 +251,8 @@ app.registerExtension({
|
|||||||
} else if (this.outputs.length > 1) {
|
} else if (this.outputs.length > 1) {
|
||||||
// Widget values empty but serialized dynamic outputs exist — sync widgets
|
// Widget values empty but serialized dynamic outputs exist — sync widgets
|
||||||
const dynamicOutputs = this.outputs.slice(1);
|
const dynamicOutputs = this.outputs.slice(1);
|
||||||
if (okWidget) okWidget.value = dynamicOutputs.map(o => o.name).join(",");
|
if (okWidget) okWidget.value = JSON.stringify(dynamicOutputs.map(o => o.name));
|
||||||
if (otWidget) otWidget.value = dynamicOutputs.map(o => o.type).join(",");
|
if (otWidget) otWidget.value = JSON.stringify(dynamicOutputs.map(o => o.type));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setSize(this.computeSize());
|
this.setSize(this.computeSize());
|
||||||
|
|||||||
Reference in New Issue
Block a user