vargha commited on
Commit
727e6a6
·
1 Parent(s): f7ef7d3

fixed multiple trimming logic

Browse files
components/dashboard_page.py CHANGED
@@ -322,22 +322,14 @@ class DashboardPage:
322
 
323
  if not user_id:
324
  log.warning("load_all_items_fn: user_id not found in session. Dashboard will display default state until login completes and data is refreshed.")
325
- # Prepare default/empty values for all outputs of show_current_item_fn
326
- # (tts_id, filename, sentence, ann_text, audio_placeholder,
327
- # trim_start_sec_ui, trim_end_sec_ui,
328
- # applied_trims_list_state_val, trims_display_val, audio_update_obj)
329
  empty_item_display_tuple = ("", "", "", "", None, None, None, [], self._convert_trims_to_df_data([]), gr.update(value=None, autoplay=False))
330
-
331
- # load_all_items_fn returns: [items_to_load, initial_idx] + list(initial_ui_values_tuple) + [progress_str]
332
- # Total 13 values.
333
- return [[], 0] + list(empty_item_display_tuple) + ["Progress: Waiting for login..."]
334
 
335
  if user_id:
336
  with get_db() as db:
337
  try:
338
  repo = AnnotatorWorkloadRepo(db)
339
- # Get all assigned items
340
- raw_items = repo.get_tts_data_with_annotations_for_user_id(user_id)
341
 
342
  items_to_load = [
343
  {
@@ -351,24 +343,25 @@ class DashboardPage:
351
  log.info(f"Loaded {len(items_to_load)} items for user {user_name} (ID: {user_id})")
352
 
353
  # --- Resume Logic: Find first unannotated or last item ---
354
- first_unannotated_idx = -1
355
- for i, item_data in enumerate(items_to_load):
356
- if not item_data["annotated"]:
357
- first_unannotated_idx = i
358
- break
359
-
360
- if first_unannotated_idx != -1:
361
- initial_idx = first_unannotated_idx
362
- log.info(f"Resuming at first unannotated item, index: {initial_idx} (ID: {items_to_load[initial_idx]['id']})")
363
- elif items_to_load: # All annotated, start at the last one or first if only one
364
- initial_idx = len(items_to_load) - 1
365
- log.info(f"All items annotated, starting at last item, index: {initial_idx} (ID: {items_to_load[initial_idx]['id']})")
 
366
  else: # No items assigned
367
  initial_idx = 0
368
- log.info("No items assigned to user.")
369
 
370
  except Exception as e:
371
- log.error(f"Failed to load items or determine resume index for user {user_name}: {e}") # Removed exc_info=True
372
  gr.Error(f"Could not load your assigned data: {e}")
373
 
374
  initial_ui_values_tuple = show_current_item_fn(items_to_load, initial_idx, sess)
@@ -665,62 +658,62 @@ class DashboardPage:
665
  return None, gr.update(value=None, autoplay=False)
666
 
667
  sr, wav_orig = original_audio_data
 
668
 
669
  if not trims_list_sec: # No trims to apply
670
  log.info("apply_multiple_trims_fn: No trims in list, returning original audio.")
671
- return (sr, wav_orig.copy()), gr.update(value=(sr, wav_orig.copy()), autoplay=False)
 
 
672
 
673
- delete_intervals_samples = []
674
- for trim_info in trims_list_sec:
675
  start_s = trim_info.get('start_sec')
676
  end_s = trim_info.get('end_sec')
677
- if start_s is not None and end_s is not None and end_s > start_s and start_s >= 0:
678
- start_sample = int(sr * start_s)
679
- end_sample = int(sr * end_s)
680
- start_sample = max(0, min(start_sample, len(wav_orig)))
681
- end_sample = max(start_sample, min(end_sample, len(wav_orig)))
682
- if start_sample < end_sample:
683
- delete_intervals_samples.append((start_sample, end_sample))
684
- else:
685
- log.warning(f"apply_multiple_trims_fn: Invalid trim skipped: {trim_info}")
686
 
687
- if not delete_intervals_samples:
688
- log.info("apply_multiple_trims_fn: No valid trims to apply, returning original audio.")
689
- return (sr, wav_orig.copy()), gr.update(value=(sr, wav_orig.copy()), autoplay=False)
 
690
 
691
- delete_intervals_samples.sort(key=lambda x: x[0])
 
 
692
 
693
- merged_delete_intervals = []
694
- if delete_intervals_samples:
695
- current_start, current_end = delete_intervals_samples[0]
696
- for next_start, next_end in delete_intervals_samples[1:]:
697
- if next_start < current_end:
698
- current_end = max(current_end, next_end)
699
- else:
700
- merged_delete_intervals.append((current_start, current_end))
701
- current_start, current_end = next_start, next_end
702
- merged_delete_intervals.append((current_start, current_end))
703
-
704
- log.info(f"apply_multiple_trims_fn: Original wav shape: {wav_orig.shape}, Merged delete intervals (samples): {merged_delete_intervals}")
705
-
706
- kept_parts_wav = []
707
- current_pos_samples = 0
708
- for del_start, del_end in merged_delete_intervals:
709
- if del_start > current_pos_samples:
710
- kept_parts_wav.append(wav_orig[current_pos_samples:del_start])
711
- current_pos_samples = del_end
712
-
713
- if current_pos_samples < len(wav_orig):
714
- kept_parts_wav.append(wav_orig[current_pos_samples:])
715
-
716
- if not kept_parts_wav:
717
- final_wav = np.array([], dtype=wav_orig.dtype)
718
- log.info("apply_multiple_trims_fn: All audio trimmed, resulting in empty audio.")
719
- else:
720
- final_wav = np.concatenate(kept_parts_wav)
721
- log.info(f"apply_multiple_trims_fn: Final wav shape after trimming: {final_wav.shape}")
722
 
723
- return (sr, final_wav), gr.update(value=(sr, final_wav), autoplay=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
724
 
725
  def _convert_trims_to_df_data(self, trims_list_sec):
726
  if not trims_list_sec:
 
322
 
323
  if not user_id:
324
  log.warning("load_all_items_fn: user_id not found in session. Dashboard will display default state until login completes and data is refreshed.")
 
 
 
 
325
  empty_item_display_tuple = ("", "", "", "", None, None, None, [], self._convert_trims_to_df_data([]), gr.update(value=None, autoplay=False))
326
+ return [[], 0] + list(empty_item_display_tuple) + ["Progress: loading data..."]
 
 
 
327
 
328
  if user_id:
329
  with get_db() as db:
330
  try:
331
  repo = AnnotatorWorkloadRepo(db)
332
+ raw_items = repo.get_tts_data_with_annotations_for_user_id(user_id, annotator_name_for_log=user_name)
 
333
 
334
  items_to_load = [
335
  {
 
343
  log.info(f"Loaded {len(items_to_load)} items for user {user_name} (ID: {user_id})")
344
 
345
  # --- Resume Logic: Find first unannotated or last item ---
346
+ if items_to_load:
347
+ first_unannotated_idx = -1
348
+ for i, item_data in enumerate(items_to_load):
349
+ if not item_data["annotated"]:
350
+ first_unannotated_idx = i
351
+ break
352
+
353
+ if first_unannotated_idx != -1:
354
+ initial_idx = first_unannotated_idx
355
+ log.info(f"Resuming at first unannotated item, index: {initial_idx} (ID: {items_to_load[initial_idx]['id']})")
356
+ else: # All items are annotated
357
+ initial_idx = len(items_to_load) - 1
358
+ log.info(f"All items annotated, starting at last item, index: {initial_idx} (ID: {items_to_load[initial_idx]['id']})")
359
  else: # No items assigned
360
  initial_idx = 0
361
+ log.info("No items assigned to user, starting at index 0.")
362
 
363
  except Exception as e:
364
+ log.error(f"Failed to load items or determine resume index for user {user_name}: {e}")
365
  gr.Error(f"Could not load your assigned data: {e}")
366
 
367
  initial_ui_values_tuple = show_current_item_fn(items_to_load, initial_idx, sess)
 
658
  return None, gr.update(value=None, autoplay=False)
659
 
660
  sr, wav_orig = original_audio_data
661
+ current_wav = wav_orig.copy() # Start with a copy of the original waveform
662
 
663
  if not trims_list_sec: # No trims to apply
664
  log.info("apply_multiple_trims_fn: No trims in list, returning original audio.")
665
+ return (sr, current_wav), gr.update(value=(sr, current_wav), autoplay=False)
666
+
667
+ log.info(f"Applying {len(trims_list_sec)} trims sequentially. Initial shape: {current_wav.shape}, Initial duration: {len(current_wav)/sr:.3f}s")
668
 
669
+ for i, trim_info in enumerate(trims_list_sec):
 
670
  start_s = trim_info.get('start_sec')
671
  end_s = trim_info.get('end_sec')
 
 
 
 
 
 
 
 
 
672
 
673
+ # Validate trim times for the current audio segment
674
+ if not (start_s is not None and end_s is not None and end_s > start_s and start_s >= 0):
675
+ log.warning(f"Trim {i+1}/{len(trims_list_sec)}: Invalid trim definition skipped: {trim_info}")
676
+ continue
677
 
678
+ if len(current_wav) == 0:
679
+ log.warning(f"Trim {i+1}/{len(trims_list_sec)}: Audio is already empty, skipping remaining trims.")
680
+ break # No more audio to trim
681
 
682
+ current_duration_s = len(current_wav) / sr
683
+ log.info(f"Trim {i+1}: Processing trim {trim_info} on audio of current duration {current_duration_s:.3f}s.")
684
+
685
+ # Convert seconds to sample indices for the current waveform
686
+ start_sample = int(sr * start_s)
687
+ end_sample = int(sr * end_s)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
688
 
689
+ current_len_samples = len(current_wav)
690
+
691
+ # Clamp sample indices to the bounds of the current waveform
692
+ # Ensure start_sample is not past the end of the current audio
693
+ start_sample = max(0, min(start_sample, current_len_samples))
694
+ # Ensure end_sample is not past the end, and not before start_sample
695
+ end_sample = max(start_sample, min(end_sample, current_len_samples))
696
+
697
+ if start_sample < end_sample: # If there's a segment to remove
698
+ log.info(f"Trim {i+1}: Applying samples {start_sample}-{end_sample} to current audio (length {current_len_samples} samples). Shape before: {current_wav.shape}")
699
+
700
+ part1 = current_wav[:start_sample]
701
+ part2 = current_wav[end_sample:]
702
+
703
+ if len(part1) == 0 and len(part2) == 0: # The entire current audio segment was trimmed
704
+ current_wav = np.array([], dtype=wav_orig.dtype)
705
+ elif len(part1) == 0: # Trimmed from the beginning of the current segment
706
+ current_wav = part2
707
+ elif len(part2) == 0: # Trimmed to the end of the current segment
708
+ current_wav = part1
709
+ else: # Trimmed from the middle of the current segment
710
+ current_wav = np.concatenate((part1, part2))
711
+ log.info(f"Trim {i+1}: Shape after: {current_wav.shape}, New duration: {len(current_wav)/sr:.3f}s")
712
+ else:
713
+ log.info(f"Trim {i+1}: No effective change for trim {trim_info} on current audio (start_sample >= end_sample after clamping or trim times out of bounds for current audio).")
714
+
715
+ log.info(f"Finished sequential trimming. Final shape: {current_wav.shape}, Final duration: {len(current_wav)/sr:.3f}s")
716
+ return (sr, current_wav), gr.update(value=(sr, current_wav), autoplay=False)
717
 
718
  def _convert_trims_to_df_data(self, trims_list_sec):
719
  if not trims_list_sec:
scripts/import_annotations_from_json.py CHANGED
@@ -35,7 +35,7 @@ def import_annotations(db: SQLAlchemySession, data: dict): # Changed SessionLoca
35
  tts_data_cache = {}
36
  annotator_cache = {}
37
 
38
- annotation_ids_for_trim_deletion_in_batch = [] # For batch deletion of trims
39
 
40
  # Create a mapping from JSON ID to sample data for efficient lookup
41
  samples_by_id = {s.get("id"): s for s in samples if s.get("id") is not None}
@@ -200,6 +200,11 @@ def import_annotations(db: SQLAlchemySession, data: dict): # Changed SessionLoca
200
  ).first()
201
 
202
  if annotation_obj:
 
 
 
 
 
203
  annotation_obj.annotated_sentence = final_annotated_sentence
204
  annotation_obj.annotated_at = final_annotated_at
205
  updated_count +=1
@@ -221,8 +226,8 @@ def import_annotations(db: SQLAlchemySession, data: dict): # Changed SessionLoca
221
  continue
222
 
223
  if annotation_obj.id:
224
- if annotation_obj.id not in annotation_ids_for_trim_deletion_in_batch:
225
- annotation_ids_for_trim_deletion_in_batch.append(annotation_obj.id)
226
 
227
  json_audio_trims = json_ann.get("audio_trims", [])
228
  if json_audio_trims:
@@ -260,10 +265,11 @@ def import_annotations(db: SQLAlchemySession, data: dict): # Changed SessionLoca
260
  samples_processed_in_batch += 1
261
 
262
  if samples_processed_in_batch >= BATCH_SIZE or (sample_idx == len(samples) - 1):
263
- if annotation_ids_for_trim_deletion_in_batch:
264
- log.info(f"Batch deleting trims for {len(annotation_ids_for_trim_deletion_in_batch)} annotations in current batch.")
265
- db.query(AudioTrim).filter(AudioTrim.annotation_id.in_(annotation_ids_for_trim_deletion_in_batch)).delete(synchronize_session=False)
266
- annotation_ids_for_trim_deletion_in_batch.clear()
 
267
 
268
  try:
269
  db.commit()
@@ -271,7 +277,6 @@ def import_annotations(db: SQLAlchemySession, data: dict): # Changed SessionLoca
271
  except Exception as e_commit:
272
  db.rollback()
273
  log.error(f"Failed to commit batch after sample index {sample_idx} (TTSData JSON ID {current_sample_json_id}): {e_commit}. Rolling back this batch.")
274
- annotation_ids_for_trim_deletion_in_batch.clear()
275
  finally:
276
  samples_processed_in_batch = 0 # Reset for next batch or end
277
 
 
35
  tts_data_cache = {}
36
  annotator_cache = {}
37
 
38
+ # annotation_ids_for_trim_deletion_in_batch = [] # Removed
39
 
40
  # Create a mapping from JSON ID to sample data for efficient lookup
41
  samples_by_id = {s.get("id"): s for s in samples if s.get("id") is not None}
 
200
  ).first()
201
 
202
  if annotation_obj:
203
+ # If annotation exists, delete its old trims first
204
+ if annotation_obj.id:
205
+ # log.debug(f"Deleting existing trims for Annotation ID {annotation_obj.id} before updating.")
206
+ db.query(AudioTrim).filter(AudioTrim.annotation_id == annotation_obj.id).delete(synchronize_session=False)
207
+
208
  annotation_obj.annotated_sentence = final_annotated_sentence
209
  annotation_obj.annotated_at = final_annotated_at
210
  updated_count +=1
 
226
  continue
227
 
228
  if annotation_obj.id:
229
+ # Removed: if annotation_obj.id not in annotation_ids_for_trim_deletion_in_batch:
230
+ # Removed: annotation_ids_for_trim_deletion_in_batch.append(annotation_obj.id)
231
 
232
  json_audio_trims = json_ann.get("audio_trims", [])
233
  if json_audio_trims:
 
265
  samples_processed_in_batch += 1
266
 
267
  if samples_processed_in_batch >= BATCH_SIZE or (sample_idx == len(samples) - 1):
268
+ # Removed the block for batch deleting trims that used annotation_ids_for_trim_deletion_in_batch
269
+ # if annotation_ids_for_trim_deletion_in_batch:
270
+ # log.info(f"Batch deleting trims for {len(annotation_ids_for_trim_deletion_in_batch)} annotations in current batch.")
271
+ # db.query(AudioTrim).filter(AudioTrim.annotation_id.in_(annotation_ids_for_trim_deletion_in_batch)).delete(synchronize_session=False)
272
+ # annotation_ids_for_trim_deletion_in_batch.clear()
273
 
274
  try:
275
  db.commit()
 
277
  except Exception as e_commit:
278
  db.rollback()
279
  log.error(f"Failed to commit batch after sample index {sample_idx} (TTSData JSON ID {current_sample_json_id}): {e_commit}. Rolling back this batch.")
 
280
  finally:
281
  samples_processed_in_batch = 0 # Reset for next batch or end
282