Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
Update state.py
Browse files
state.py
CHANGED
|
@@ -55,7 +55,6 @@ def init_session_state():
|
|
| 55 |
"speakerSegments": {}, # {filename: {speaker: [(start,end), ...]}}
|
| 56 |
"speakerWaveforms": {}, # {filename: (waveform_tensor, sample_rate)}
|
| 57 |
"globalRenames": [], # [{"name": str, "speakers": ["file: SPEAKER_##", ...]}]
|
| 58 |
-
"globally_tracked_tokens": set(), # tokens ever assigned via globalRenames
|
| 59 |
"analyzeAllToggle": False,
|
| 60 |
}
|
| 61 |
for key, value in defaults.items():
|
|
@@ -139,78 +138,34 @@ def updateCategoryOptions():
|
|
| 139 |
# Global rename callbacks
|
| 140 |
# ---------------------------------------------------------------------------
|
| 141 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 142 |
def _global_rename_key(index):
|
| 143 |
return f"grename_speakers_{index}"
|
| 144 |
|
| 145 |
|
| 146 |
-
def
|
| 147 |
-
"""
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
via globalRenames is recorded there. On each call:
|
| 151 |
-
- token currently assigned → rename to the entry's name
|
| 152 |
-
- token in graveyard but NOT currently assigned → revert to raw label
|
| 153 |
-
- token never touched by globalRenames → leave speakerRenames untouched
|
| 154 |
-
(inline-only rename)
|
| 155 |
"""
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
# 1. Update speakerRenames for tokens already in the graveyard
|
| 166 |
-
for fname in list(st.session_state.speakerRenames.keys()):
|
| 167 |
-
renames = st.session_state.speakerRenames[fname]
|
| 168 |
-
for sp in list(renames.keys()):
|
| 169 |
-
token = f"{fname}: {sp}"
|
| 170 |
-
if token in token_to_name:
|
| 171 |
-
# Currently assigned — apply the name
|
| 172 |
-
renames[sp] = token_to_name[token]
|
| 173 |
-
elif token in st.session_state.globally_tracked_tokens:
|
| 174 |
-
# Was global but is now unassigned — revert to raw label
|
| 175 |
-
del renames[sp]
|
| 176 |
-
# else: purely inline rename — leave untouched
|
| 177 |
-
|
| 178 |
-
# 2. Apply newly assigned tokens not yet in speakerRenames
|
| 179 |
-
for token, name in token_to_name.items():
|
| 180 |
-
if ": " not in token:
|
| 181 |
-
continue
|
| 182 |
-
fname, raw_sp = token.split(": ", 1)
|
| 183 |
-
if fname in st.session_state.speakerRenames:
|
| 184 |
-
st.session_state.speakerRenames[fname][raw_sp] = name
|
| 185 |
-
|
| 186 |
-
# 3. Sync inline-tab widget keys so Rename Speaker tab reflects changes
|
| 187 |
-
for fname, renames in st.session_state.speakerRenames.items():
|
| 188 |
-
results = st.session_state.results.get(fname)
|
| 189 |
-
if not results or len(results) != 2:
|
| 190 |
-
continue
|
| 191 |
-
for sp in results[0].labels():
|
| 192 |
-
st.session_state[f"rename_{fname}_{sp}"] = renames.get(sp, "")
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
def enforceGlobalRenameExclusivity(changed_idx):
|
| 196 |
-
"""Ensure a token assigned to entry[changed_idx] is removed from all others."""
|
| 197 |
-
changed_tokens = set(st.session_state.globalRenames[changed_idx]["speakers"])
|
| 198 |
-
for idx, entry in enumerate(st.session_state.globalRenames):
|
| 199 |
-
if idx == changed_idx:
|
| 200 |
-
continue
|
| 201 |
-
new_speakers = [t for t in entry["speakers"] if t not in changed_tokens]
|
| 202 |
-
if new_speakers != entry["speakers"]:
|
| 203 |
-
entry["speakers"] = new_speakers
|
| 204 |
-
# Clear widget key so it re-seeds from updated entry data on next render
|
| 205 |
-
st.session_state.pop(_global_rename_key(idx), None)
|
| 206 |
-
applyGlobalRenames()
|
| 207 |
|
| 208 |
|
| 209 |
def addGlobalRename():
|
| 210 |
new_name = st.session_state.globalRenameInput.strip()
|
| 211 |
if not new_name:
|
| 212 |
return
|
| 213 |
-
# Don't create a duplicate entry — if the name already exists just clear the input
|
| 214 |
for entry in st.session_state.globalRenames:
|
| 215 |
if entry["name"] == new_name:
|
| 216 |
st.toast(f"'{new_name}' already exists in the rename list")
|
|
@@ -218,69 +173,96 @@ def addGlobalRename():
|
|
| 218 |
return
|
| 219 |
st.toast(f"Adding rename '{new_name}'")
|
| 220 |
st.session_state.globalRenames.append({"name": new_name, "speakers": []})
|
| 221 |
-
# Don't pre-seed the widget key here — the render loop will seed it from
|
| 222 |
-
# entry["speakers"] on the next pass, which avoids any widget-mutation error.
|
| 223 |
st.session_state.globalRenameInput = ""
|
| 224 |
|
| 225 |
|
| 226 |
def removeGlobalRename(index):
|
| 227 |
entry = st.session_state.globalRenames[index]
|
| 228 |
st.toast(f"Removing rename '{entry['name']}'")
|
| 229 |
-
#
|
| 230 |
-
|
|
|
|
| 231 |
st.session_state.pop(_global_rename_key(index), None)
|
| 232 |
del st.session_state.globalRenames[index]
|
| 233 |
-
# Shift remaining widget keys down
|
| 234 |
for i in range(index, len(st.session_state.globalRenames)):
|
| 235 |
st.session_state[_global_rename_key(i)] = list(
|
| 236 |
st.session_state.globalRenames[i]["speakers"]
|
| 237 |
)
|
| 238 |
-
applyGlobalRenames()
|
| 239 |
|
| 240 |
|
| 241 |
def apply_inline_rename(currFile, raw_sp, new_name):
|
| 242 |
-
"""Write a
|
| 243 |
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
"""
|
| 248 |
new_name = new_name.strip()
|
| 249 |
token = f"{currFile}: {raw_sp}"
|
| 250 |
|
| 251 |
-
# Remove
|
| 252 |
-
# must belong to at most one name. Clear the sidebar widget key so it
|
| 253 |
-
# re-seeds from the updated entry data on the next render pass.
|
| 254 |
for idx, entry in enumerate(st.session_state.globalRenames):
|
| 255 |
if token in entry["speakers"]:
|
| 256 |
entry["speakers"].remove(token)
|
| 257 |
st.session_state.pop(_global_rename_key(idx), None)
|
| 258 |
|
| 259 |
if new_name:
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
# Sync the inline-tab text widget so the tab shows the confirmed name
|
| 263 |
-
st.session_state[f"rename_{currFile}_{raw_sp}"] = new_name
|
| 264 |
-
# Record this token as globally tracked so applyGlobalRenames can
|
| 265 |
-
# revert it if it's ever removed from globalRenames.
|
| 266 |
-
st.session_state.globally_tracked_tokens.add(token)
|
| 267 |
-
# Find an existing globalRenames entry for this name, or create one
|
| 268 |
for idx, entry in enumerate(st.session_state.globalRenames):
|
| 269 |
if entry["name"] == new_name:
|
| 270 |
entry["speakers"].append(token)
|
| 271 |
st.session_state.pop(_global_rename_key(idx), None)
|
| 272 |
return
|
| 273 |
-
# No existing entry — create a new one; don't pre-set widget key,
|
| 274 |
-
# the render loop will seed it from entry["speakers"] on the next pass.
|
| 275 |
st.session_state.globalRenames.append({"name": new_name, "speakers": [token]})
|
| 276 |
else:
|
| 277 |
-
|
| 278 |
-
st.
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 284 |
|
| 285 |
|
| 286 |
# ---------------------------------------------------------------------------
|
|
@@ -293,18 +275,10 @@ def updateMultiSelect():
|
|
| 293 |
result = st.session_state.results.get(fileName)
|
| 294 |
if not result:
|
| 295 |
return
|
| 296 |
-
currAnnotation, _ = result
|
| 297 |
-
speakerNames = list(currAnnotation.labels())
|
| 298 |
-
saved_renames = st.session_state.speakerRenames.get(fileName, {})
|
| 299 |
-
for sp in speakerNames:
|
| 300 |
-
saved = saved_renames.get(sp, "")
|
| 301 |
-
st.session_state[f"rename_{fileName}_{sp}"] = saved
|
| 302 |
# Pop category widget keys so they re-seed from categorySelect data
|
| 303 |
for category in st.session_state.categories:
|
| 304 |
st.session_state.pop(f"multiselect_{category}", None)
|
| 305 |
-
# Pop globalRenames widget keys so they re-seed from entry["speakers"] data
|
| 306 |
-
# This prevents a file-switch from leaving stale selections in the sidebar
|
| 307 |
-
# rename multiselects.
|
| 308 |
for i in range(len(st.session_state.globalRenames)):
|
| 309 |
st.session_state.pop(_global_rename_key(i), None)
|
| 310 |
|
|
|
|
| 55 |
"speakerSegments": {}, # {filename: {speaker: [(start,end), ...]}}
|
| 56 |
"speakerWaveforms": {}, # {filename: (waveform_tensor, sample_rate)}
|
| 57 |
"globalRenames": [], # [{"name": str, "speakers": ["file: SPEAKER_##", ...]}]
|
|
|
|
| 58 |
"analyzeAllToggle": False,
|
| 59 |
}
|
| 60 |
for key, value in defaults.items():
|
|
|
|
| 138 |
# Global rename callbacks
|
| 139 |
# ---------------------------------------------------------------------------
|
| 140 |
|
| 141 |
+
# ---------------------------------------------------------------------------
|
| 142 |
+
# Global rename helpers
|
| 143 |
+
# ---------------------------------------------------------------------------
|
| 144 |
+
|
| 145 |
def _global_rename_key(index):
|
| 146 |
return f"grename_speakers_{index}"
|
| 147 |
|
| 148 |
|
| 149 |
+
def _write_rename(token, name):
|
| 150 |
+
"""Write name into speakerRenames for a single token (fname: SPEAKER_##).
|
| 151 |
+
If name is empty, clears the entry (revert to raw label).
|
| 152 |
+
Silently ignores tokens that don't match a known file.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 153 |
"""
|
| 154 |
+
if ": " not in token:
|
| 155 |
+
return
|
| 156 |
+
fname, raw_sp = token.split(": ", 1)
|
| 157 |
+
if fname not in st.session_state.speakerRenames:
|
| 158 |
+
return # token references an unknown file — ignore
|
| 159 |
+
if name:
|
| 160 |
+
st.session_state.speakerRenames[fname][raw_sp] = name
|
| 161 |
+
else:
|
| 162 |
+
st.session_state.speakerRenames[fname].pop(raw_sp, None)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 163 |
|
| 164 |
|
| 165 |
def addGlobalRename():
|
| 166 |
new_name = st.session_state.globalRenameInput.strip()
|
| 167 |
if not new_name:
|
| 168 |
return
|
|
|
|
| 169 |
for entry in st.session_state.globalRenames:
|
| 170 |
if entry["name"] == new_name:
|
| 171 |
st.toast(f"'{new_name}' already exists in the rename list")
|
|
|
|
| 173 |
return
|
| 174 |
st.toast(f"Adding rename '{new_name}'")
|
| 175 |
st.session_state.globalRenames.append({"name": new_name, "speakers": []})
|
|
|
|
|
|
|
| 176 |
st.session_state.globalRenameInput = ""
|
| 177 |
|
| 178 |
|
| 179 |
def removeGlobalRename(index):
|
| 180 |
entry = st.session_state.globalRenames[index]
|
| 181 |
st.toast(f"Removing rename '{entry['name']}'")
|
| 182 |
+
# Revert every speaker that belonged to this entry
|
| 183 |
+
for token in entry["speakers"]:
|
| 184 |
+
_write_rename(token, "")
|
| 185 |
st.session_state.pop(_global_rename_key(index), None)
|
| 186 |
del st.session_state.globalRenames[index]
|
| 187 |
+
# Shift remaining widget keys down
|
| 188 |
for i in range(index, len(st.session_state.globalRenames)):
|
| 189 |
st.session_state[_global_rename_key(i)] = list(
|
| 190 |
st.session_state.globalRenames[i]["speakers"]
|
| 191 |
)
|
|
|
|
| 192 |
|
| 193 |
|
| 194 |
def apply_inline_rename(currFile, raw_sp, new_name):
|
| 195 |
+
"""Write a rename from the Rename Speaker tab into speakerRenames and globalRenames.
|
| 196 |
|
| 197 |
+
- Removes the token from any existing globalRenames entry first (exclusivity).
|
| 198 |
+
- If new_name is non-empty: writes the rename and upserts a globalRenames entry.
|
| 199 |
+
- If new_name is empty: clears the rename (revert to SPEAKER_##).
|
| 200 |
"""
|
| 201 |
new_name = new_name.strip()
|
| 202 |
token = f"{currFile}: {raw_sp}"
|
| 203 |
|
| 204 |
+
# Remove from any existing globalRenames entry
|
|
|
|
|
|
|
| 205 |
for idx, entry in enumerate(st.session_state.globalRenames):
|
| 206 |
if token in entry["speakers"]:
|
| 207 |
entry["speakers"].remove(token)
|
| 208 |
st.session_state.pop(_global_rename_key(idx), None)
|
| 209 |
|
| 210 |
if new_name:
|
| 211 |
+
_write_rename(token, new_name)
|
| 212 |
+
# Upsert a globalRenames entry for this name
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 213 |
for idx, entry in enumerate(st.session_state.globalRenames):
|
| 214 |
if entry["name"] == new_name:
|
| 215 |
entry["speakers"].append(token)
|
| 216 |
st.session_state.pop(_global_rename_key(idx), None)
|
| 217 |
return
|
|
|
|
|
|
|
| 218 |
st.session_state.globalRenames.append({"name": new_name, "speakers": [token]})
|
| 219 |
else:
|
| 220 |
+
_write_rename(token, "")
|
| 221 |
+
st.toast(f"Reverted {raw_sp} to original label")
|
| 222 |
+
|
| 223 |
+
|
| 224 |
+
def on_grename_change(idx):
|
| 225 |
+
"""Callback for the sidebar rename multiselect at position idx.
|
| 226 |
+
|
| 227 |
+
Diffs old vs new speakers list:
|
| 228 |
+
- Newly added tokens → enforce exclusivity across other entries (evict + revert
|
| 229 |
+
their old name), then write this entry's name into speakerRenames.
|
| 230 |
+
- Removed tokens → clear speakerRenames for those tokens (revert to SPEAKER_##).
|
| 231 |
+
"""
|
| 232 |
+
grkey = _global_rename_key(idx)
|
| 233 |
+
entry = st.session_state.globalRenames[idx]
|
| 234 |
+
name = entry["name"]
|
| 235 |
+
|
| 236 |
+
prev = set(entry["speakers"])
|
| 237 |
+
new = set(st.session_state.get(grkey, []))
|
| 238 |
+
|
| 239 |
+
added = new - prev
|
| 240 |
+
removed = prev - new
|
| 241 |
+
|
| 242 |
+
# Write the new speakers list to the data model (keep stable order: prev kept +
|
| 243 |
+
# new additions appended, so the multiselect doesn't jump around)
|
| 244 |
+
kept = [t for t in entry["speakers"] if t in new]
|
| 245 |
+
entry["speakers"] = kept + [t for t in added]
|
| 246 |
+
# Pop widget key so it re-seeds cleanly from entry["speakers"] on next render
|
| 247 |
+
st.session_state.pop(grkey, None)
|
| 248 |
+
|
| 249 |
+
# Enforce exclusivity and apply renames for newly added tokens
|
| 250 |
+
for token in added:
|
| 251 |
+
# Evict token from every other entry and revert its old name
|
| 252 |
+
for other_idx, other_entry in enumerate(st.session_state.globalRenames):
|
| 253 |
+
if other_idx == idx:
|
| 254 |
+
continue
|
| 255 |
+
if token in other_entry["speakers"]:
|
| 256 |
+
other_entry["speakers"].remove(token)
|
| 257 |
+
st.session_state.pop(_global_rename_key(other_idx), None)
|
| 258 |
+
# Revert the old name before we overwrite with the new one
|
| 259 |
+
_write_rename(token, "")
|
| 260 |
+
# Now write the new name
|
| 261 |
+
_write_rename(token, name)
|
| 262 |
+
|
| 263 |
+
# Revert speakerRenames for tokens that were removed from this entry
|
| 264 |
+
for token in removed:
|
| 265 |
+
_write_rename(token, "")
|
| 266 |
|
| 267 |
|
| 268 |
# ---------------------------------------------------------------------------
|
|
|
|
| 275 |
result = st.session_state.results.get(fileName)
|
| 276 |
if not result:
|
| 277 |
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 278 |
# Pop category widget keys so they re-seed from categorySelect data
|
| 279 |
for category in st.session_state.categories:
|
| 280 |
st.session_state.pop(f"multiselect_{category}", None)
|
| 281 |
+
# Pop globalRenames widget keys so they re-seed from entry["speakers"] data
|
|
|
|
|
|
|
| 282 |
for i in range(len(st.session_state.globalRenames)):
|
| 283 |
st.session_state.pop(_global_rename_key(i), None)
|
| 284 |
|