cockolo terada commited on
Commit
bd8dfd2
·
verified ·
1 Parent(s): 5675a23

Update gradio_tabs/single.py

Browse files
Files changed (1) hide show
  1. gradio_tabs/single.py +72 -81
gradio_tabs/single.py CHANGED
@@ -72,6 +72,14 @@ class TTSModelHolder:
72
  }
73
  with open(model2_path / "style_settings.json", "w", encoding="utf-8") as f:
74
  json.dump(style_settings_data, f, indent=2, ensure_ascii=False)
 
 
 
 
 
 
 
 
75
 
76
  def refresh(self) -> List[str]:
77
  """
@@ -79,6 +87,7 @@ class TTSModelHolder:
79
  更新後のモデルリストを返す。
80
  """
81
  if self.root_dir.is_dir():
 
82
  self.model_names = sorted([d.name for d in self.root_dir.iterdir() if d.is_dir()])
83
  print(f"TTSModelHolder model list refreshed. Known models: {self.model_names}")
84
  else:
@@ -92,7 +101,7 @@ class TTSModelHolder:
92
  error_msg = (
93
  f"Model '{model_name}' is not in the known list of TTSModelHolder. "
94
  f"Current list: {self.model_names}. "
95
- "Please refresh the model list by toggling the symlink checkbox or clicking the refresh button."
96
  )
97
  print(f"[ERROR] {error_msg}")
98
  raise ValueError(error_msg)
@@ -418,7 +427,9 @@ def process_single_synthesis_webui(
418
  # ▲▲▲ 変更点 ▲▲▲
419
 
420
  def create_synthesis_app(model_holder: TTSModelHolder) -> gr.Blocks:
421
- is_shm_available = sys.platform != "win32" and Path("/dev/shm").exists() and Path("/dev/shm").is_dir()
 
 
422
 
423
  custom_css = """
424
  .audio-output-row { display: flex !important; flex-wrap: wrap !important; gap: 10px !important; }
@@ -434,7 +445,7 @@ def create_synthesis_app(model_holder: TTSModelHolder) -> gr.Blocks:
434
  with gr.Blocks(css=custom_css) as app:
435
  MAX_AUDIO_OUTPUTS = 4
436
  ITEMS_PER_ROW = 4
437
- # ▼▼▼ 変更点: 作業台の最大アイテム数を8に変更 ▼▼▼
438
  MAX_WORKBENCH_ITEMS = 8
439
  # ▲▲▲ 変更点 ▲▲▲
440
 
@@ -445,7 +456,7 @@ def create_synthesis_app(model_holder: TTSModelHolder) -> gr.Blocks:
445
  # saved_audio_hashes_state = gr.State(set())
446
  # ▲▲▲ 変更点 ▲▲▲
447
 
448
- # --- 作業台UI更新ヘルパー ---
449
  def update_workbench_ui(workbench_list: List[Dict]) -> Tuple:
450
  updates = []
451
  for i in range(MAX_WORKBENCH_ITEMS):
@@ -510,9 +521,9 @@ def create_synthesis_app(model_holder: TTSModelHolder) -> gr.Blocks:
510
  type="filepath", interactive=False
511
  ))
512
  with gr.Row():
513
- # ▼▼▼ 変更点: 保存ボタンを削除し、作業台ボタンの幅を広げる ▼▼▼
514
  # save_buttons.append(gr.Button("💾 保存", scale=1))
515
- to_workbench_buttons.append(gr.Button("🛠️ 作業台に追加", scale=2))
516
  # ▲▲▲ 変更点 ▲▲▲
517
  audio_item_columns.append(audio_col)
518
 
@@ -533,10 +544,12 @@ def create_synthesis_app(model_holder: TTSModelHolder) -> gr.Blocks:
533
  status_textbox = gr.Textbox(interactive=False, lines=5, max_lines=5, autoscroll=True, show_label=False, placeholder="ここにログが表示されます...")
534
 
535
  with gr.Column(scale=1):
 
536
  with gr.Row():
537
- use_symlink_mode_checkbox = gr.Checkbox(label="融☆合モデルを使う", value=False, interactive=True, scale=3, visible=is_shm_available)
538
  refresh_model_list_button = gr.Button("再読込", scale=1)
539
- selected_model_dropdown = gr.Dropdown(label="話者", choices=[], value=None, interactive=True)
 
540
  # ▼▼▼ 変更点: モデルファイル選択ドロップダウンを削除 ▼▼▼
541
  # selected_model_file_dropdown = gr.Dropdown(label="モデルファイル (.safetensors)", choices=[], value=None, interactive=True)
542
  # ▲▲▲ 変更点 ▲▲▲
@@ -566,9 +579,9 @@ def create_synthesis_app(model_holder: TTSModelHolder) -> gr.Blocks:
566
  random_text_mode_slider = gr.Slider(label="分割の単位", minimum=1, maximum=4, value=1, step=1, info="1:形態素, 2:チャンク, 3:文節, 4:節", interactive=True)
567
  random_text_ratio_textbox = gr.Textbox(label="カタカナ化の割合", value="0.5, 1", info="カンマ区切りで複数指定可。指定値からランダムに1つ使用。", interactive=True)
568
 
569
- # ▼▼▼ 変更点 2: 作業台のレイアウトを2列に変更 ▼▼▼
570
- with gr.Tab("作業台"):
571
- gr.Markdown("## 作業台\n読み上げタブで生成した音声をここにストックし、結合や保存ができます。最大8個まで保持できます。")
572
 
573
  workbench_items = []
574
  all_workbench_ui_components = []
@@ -599,16 +612,16 @@ def create_synthesis_app(model_holder: TTSModelHolder) -> gr.Blocks:
599
 
600
  with gr.Row():
601
  merge_preview_button = gr.Button("1.結合&プレビュー", variant="primary")
602
- add_merged_to_workbench_button = gr.Button("2.作業台に追加", variant="primary")
603
  delete_originals_checkbox = gr.Checkbox(label="結合時に自動で元ファイルを削除", value=False, interactive=True)
604
 
605
  preview_audio_player = gr.Audio(label="結合結果プレビュー", interactive=False, type="filepath")
606
 
607
- # ▼▼▼ 変更点: 作業台の保存機能を削除 ▼▼▼
608
  # gr.Markdown("---")
609
  #
610
  # with gr.Blocks():
611
- # gr.Markdown("#### 作業台の音声を保存")
612
  # with gr.Row():
613
  # with gr.Column(scale=1):
614
  # audio_to_save_num_input = gr.Number(label="保存する音声の番号", value=1, minimum=1, step=1, precision=0, interactive=True)
@@ -674,52 +687,23 @@ def create_synthesis_app(model_holder: TTSModelHolder) -> gr.Blocks:
674
  default_weight = styles_map[first_key].get("weight", DEFAULT_STYLE_WEIGHT)
675
  return gr.update(choices=display_names, value=default_display_name), gr.update(value=default_weight), styles_map
676
 
677
- def action_refresh_model_list(use_symlink_mode: bool):
 
678
  """モデルリストを再読み込みし、UIとバックエンドの状態を同期させる。"""
679
- SHM_PATH = Path("/dev/shm")
680
-
681
- # 既存のシンボリックリンクをクリアする処理
682
- if assets_root_path.exists():
683
- for item in assets_root_path.iterdir():
684
- if item.is_symlink():
685
- try:
686
- item.unlink()
687
- except OSError as e:
688
- print(f"Failed to remove symlink {item}: {e}")
689
-
690
- # シンボリックリンクモードが有効なら、新しいリンクを作成する処理
691
- if use_symlink_mode:
692
- if SHM_PATH.exists() and SHM_PATH.is_dir():
693
- for item in SHM_PATH.iterdir():
694
- if item.is_dir():
695
- target_link = assets_root_path / item.name
696
- if not target_link.exists():
697
- try:
698
- os.symlink(item, target_link)
699
- except OSError as e:
700
- print(f"Warning: Could not create symlink for {item.name}: {e}")
701
- else:
702
- print(f"Warning: Symlink mode is on, but {SHM_PATH} does not exist or is not a directory.")
703
-
704
  # バックエンドのリストを更新
705
  model_holder.refresh()
706
-
707
- # ▼▼▼ 変更点: モデルファイル選択ロジックの削除に伴い、UI更新を簡略化 ▼▼▼
708
- if use_symlink_mode:
709
- ui_model_list = [p.name for p in assets_root_path.iterdir() if p.is_symlink()]
710
- formatted_choices = format_and_sort_model_names(ui_model_list)
711
- value = formatted_choices[0][1] if formatted_choices else None
712
- model_dropdown_update = gr.update(choices=formatted_choices, value=value)
713
- else:
714
- ui_model_list = [p.name for p in assets_root_path.iterdir() if p.is_dir() and not p.is_symlink()]
715
- sorted_list = sorted(ui_model_list)
716
- value = sorted_list[0] if sorted_list else None
717
- model_dropdown_update = gr.update(choices=sorted_list, value=value)
718
 
719
  style_dropdown_update, style_weight_update, styles_data_state_update = load_styles_for_ui(value)
720
 
721
  return model_dropdown_update, style_dropdown_update, style_weight_update, styles_data_state_update
722
- # ▲▲▲ 変更点 ▲▲▲
723
 
724
  def on_model_select_change(selected_model_name: Optional[str]):
725
  # ▼▼▼ 変更点: モデルファイル選択UIがなくなったため、関連処理を削除 ▼▼▼
@@ -913,7 +897,7 @@ def create_synthesis_app(model_holder: TTSModelHolder) -> gr.Blocks:
913
 
914
  return tuple(final_outputs)
915
 
916
- # --- 作業台イベントハンドラ ---
917
  def add_to_workbench(
918
  current_status: str,
919
  current_workbench_list: List[Dict],
@@ -923,12 +907,12 @@ def create_synthesis_app(model_holder: TTSModelHolder) -> gr.Blocks:
923
  safe_workbench_list = current_workbench_list or []
924
 
925
  if not audio_path or not Path(audio_path).exists():
926
- log_messages.append("⚠️ [作業台追加エラー] 追加する音声ファイルが見つかりません。")
927
  final_status = (current_status + "\n" + "\n".join(log_messages)).strip()
928
  return (final_status, safe_workbench_list) + update_workbench_ui(safe_workbench_list)
929
 
930
  if any(item['audio_path'] == audio_path for item in safe_workbench_list):
931
- log_messages.append("ℹ️ この音声はすでに作業台に存在します。")
932
  final_status = (current_status + "\n" + "\n".join(log_messages)).strip()
933
  return (final_status, safe_workbench_list) + update_workbench_ui(safe_workbench_list)
934
 
@@ -957,10 +941,10 @@ def create_synthesis_app(model_holder: TTSModelHolder) -> gr.Blocks:
957
  path_to_delete.unlink()
958
  except Exception as e:
959
  print(f"Warning: Failed to delete old workbench audio file: {e}")
960
- log_messages.append(f"ℹ️ 作業台のアイテムが最大数({MAX_WORKBENCH_ITEMS})に達したため、一番古いアイテムを削除しました。")
961
 
962
  ui_updates = update_workbench_ui(updated_list)
963
- log_messages.append("✅ 作業台に音声を追加しました。")
964
  final_status = (current_status + "\n" + "\n".join(log_messages)).strip()
965
  return (final_status, updated_list) + ui_updates
966
 
@@ -975,13 +959,13 @@ def create_synthesis_app(model_holder: TTSModelHolder) -> gr.Blocks:
975
  path_to_delete = Path(item_to_remove['audio_path'])
976
  if path_to_delete.exists() and str(path_to_delete.parent) == tempfile.gettempdir():
977
  path_to_delete.unlink()
978
- log_messages.append(f"✅ 作業台からアイテム #{index_to_remove + 1} を削除し、一時ファイルをクリーンアップしました。")
979
  elif path_to_delete.exists():
980
- log_messages.append(f"✅ 作業台からアイテム #{index_to_remove + 1} を削除しました。(ファイルは保持: {path_to_delete.name})")
981
  else:
982
- log_messages.append(f"✅ 作業台からアイテム #{index_to_remove + 1} を削除しました。(関連ファイルなし)")
983
  except Exception as e:
984
- log_messages.append(f"⚠️ 作業台のアイテム #{index_to_remove + 1} のファイル削除中にエラー: {e}")
985
 
986
  updated_list = [item for i, item in enumerate(safe_workbench_list) if i != index_to_remove]
987
  ui_updates = update_workbench_ui(updated_list)
@@ -996,7 +980,7 @@ def create_synthesis_app(model_holder: TTSModelHolder) -> gr.Blocks:
996
  ):
997
  log_messages = []
998
  if not workbench_list:
999
- log_messages.append("⚠️ [結合プレビュー警告] 作業台に音声がありません。")
1000
  final_status = (current_status + "\n" + "\n".join(log_messages)).strip()
1001
  return final_status, None, {}
1002
 
@@ -1096,13 +1080,13 @@ def create_synthesis_app(model_holder: TTSModelHolder) -> gr.Blocks:
1096
  log_messages = []
1097
  safe_workbench_list = current_workbench_list or []
1098
  if not preview_data or "audio_path" not in preview_data:
1099
- log_messages.append("⚠️ [作業台追加エラー] 追加する結合済み音声がありません。先にプレビューを生成してください。")
1100
  final_status = (current_status + "\n" + "\n".join(log_messages)).strip()
1101
  return (final_status, safe_workbench_list) + update_workbench_ui(safe_workbench_list)
1102
 
1103
  src_path = Path(preview_data["audio_path"])
1104
  if not src_path.exists():
1105
- log_messages.append("⚠️ [作業台追加エラー] 結合済み音声ファイルが見つかりません。")
1106
  final_status = (current_status + "\n" + "\n".join(log_messages)).strip()
1107
  return (final_status, safe_workbench_list) + update_workbench_ui(safe_workbench_list)
1108
 
@@ -1134,10 +1118,10 @@ def create_synthesis_app(model_holder: TTSModelHolder) -> gr.Blocks:
1134
  log_messages.append(f"⚠️ 元の音声ファイル削除中にエラー: {e}")
1135
 
1136
  final_workbench_list = [new_merged_item] + remaining_list
1137
- log_messages.append(f"✅ 結合音声を作業台に追加し、元の音声(#{idx1+1}, #{idx2+1})を削除しました。")
1138
  else:
1139
  final_workbench_list = [new_merged_item] + safe_workbench_list
1140
- log_messages.append("✅ 結合済みの音声を作業台の一番上に追加しました。")
1141
 
1142
  if len(final_workbench_list) > MAX_WORKBENCH_ITEMS:
1143
  item_to_remove = final_workbench_list.pop(-1)
@@ -1147,7 +1131,7 @@ def create_synthesis_app(model_holder: TTSModelHolder) -> gr.Blocks:
1147
  path_to_delete.unlink()
1148
  except Exception as e:
1149
  print(f"Warning: Failed to delete old workbench audio file: {e}")
1150
- log_messages.append(f"ℹ️ 作業台が最大数({MAX_WORKBENCH_ITEMS})に達したため一番古いアイテムを削除しました。")
1151
 
1152
  ui_updates = update_workbench_ui(final_workbench_list)
1153
  final_status = (current_status + "\n" + "\n".join(log_messages)).strip()
@@ -1156,11 +1140,11 @@ def create_synthesis_app(model_holder: TTSModelHolder) -> gr.Blocks:
1156
 
1157
  # --- イベントリスナー接続 ---
1158
  # ▼▼▼ 変更点: イベントリスナーの inputs/outputs を修正 ▼▼▼
1159
- refresh_triggers = [refresh_model_list_button.click, use_symlink_mode_checkbox.change]
1160
- for trigger in refresh_triggers:
1161
- trigger(action_refresh_model_list,
1162
- inputs=[use_symlink_mode_checkbox],
1163
- outputs=[selected_model_dropdown, current_styles_dropdown, style_weight_for_synth_slider, all_styles_data_state])
1164
 
1165
  selected_model_dropdown.change(on_model_select_change,
1166
  inputs=[selected_model_dropdown],
@@ -1179,7 +1163,7 @@ def create_synthesis_app(model_holder: TTSModelHolder) -> gr.Blocks:
1179
  fn=action_run_synthesis,
1180
  inputs=[
1181
  selected_model_dropdown, # selected_model_file_dropdown は削除
1182
- # use_symlink_mode_checkbox, # action_run_synthesisから不要になったため削除
1183
  current_styles_dropdown, style_weight_for_synth_slider,
1184
  text_input, generation_mode_radio, batch_count_slider,
1185
  language_dropdown, seed_input, speaker_name_textbox,
@@ -1225,7 +1209,7 @@ def create_synthesis_app(model_holder: TTSModelHolder) -> gr.Blocks:
1225
  outputs=[status_textbox, preview_audio_player, merged_preview_state]
1226
  )
1227
 
1228
- # ▼▼▼ 変更点: 作業台の保存ボタンのクリックイベントを削除 ▼▼▼
1229
  # save_creative_button.click(...)
1230
  # ▲▲▲ 変更点 ▲▲▲
1231
 
@@ -1244,9 +1228,11 @@ def create_synthesis_app(model_holder: TTSModelHolder) -> gr.Blocks:
1244
 
1245
  player_width_slider.release(lambda w: f"<script>document.documentElement.style.setProperty('--audio-width', '{w}px');</script>", inputs=[player_width_slider], outputs=[js_injector_html])
1246
 
1247
- app.load(action_refresh_model_list,
1248
- inputs=[use_symlink_mode_checkbox],
1249
- outputs=[selected_model_dropdown, current_styles_dropdown, style_weight_for_synth_slider, all_styles_data_state])
 
 
1250
  # ▲▲▲ 変更点 ▲▲▲
1251
  return app
1252
 
@@ -1254,7 +1240,9 @@ def create_synthesis_app(model_holder: TTSModelHolder) -> gr.Blocks:
1254
  if __name__ == "__main__":
1255
  if Path("model_assets").exists(): shutil.rmtree("model_assets")
1256
 
1257
- shm_path = Path("/dev/shm")
 
 
1258
 
1259
  mock_model_holder = TTSModelHolder()
1260
  print(f"Initial models loaded by TTSModelHolder: {mock_model_holder.model_names}")
@@ -1263,8 +1251,11 @@ if __name__ == "__main__":
1263
  assets_dir_path = assets_root_path.resolve()
1264
  assets_dir_path.mkdir(exist_ok=True)
1265
  allowed_paths = [str(assets_dir_path)]
1266
- if sys.platform != "win32" and shm_path.exists():
1267
- allowed_paths.append(str(shm_path.resolve()))
 
 
 
1268
 
1269
  output_dir_path = Path("output").resolve()
1270
  (output_dir_path / "normal").mkdir(exist_ok=True, parents=True)
 
72
  }
73
  with open(model2_path / "style_settings.json", "w", encoding="utf-8") as f:
74
  json.dump(style_settings_data, f, indent=2, ensure_ascii=False)
75
+
76
+ # Sample Merged Model
77
+ merged_model_path = p / "miku_90p_rinu_10p"
78
+ merged_model_path.mkdir(parents=True, exist_ok=True)
79
+ (merged_model_path / "G_merged.safetensors").touch()
80
+ with open(merged_model_path / "config.json", "w", encoding="utf-8") as f:
81
+ json.dump(config1, f, indent=2)
82
+
83
 
84
  def refresh(self) -> List[str]:
85
  """
 
87
  更新後のモデルリストを返す。
88
  """
89
  if self.root_dir.is_dir():
90
+ # is_dir()はシンボリックリンクされたディレクトリもTrueを返す
91
  self.model_names = sorted([d.name for d in self.root_dir.iterdir() if d.is_dir()])
92
  print(f"TTSModelHolder model list refreshed. Known models: {self.model_names}")
93
  else:
 
101
  error_msg = (
102
  f"Model '{model_name}' is not in the known list of TTSModelHolder. "
103
  f"Current list: {self.model_names}. "
104
+ "Please refresh the model list by clicking the refresh button."
105
  )
106
  print(f"[ERROR] {error_msg}")
107
  raise ValueError(error_msg)
 
427
  # ▲▲▲ 変更点 ▲▲▲
428
 
429
  def create_synthesis_app(model_holder: TTSModelHolder) -> gr.Blocks:
430
+ # ▼▼▼ 変更点: is_shm_available 変数を削除 ▼▼▼
431
+ # is_shm_available = sys.platform != "win32" and Path("/dev/shm").exists() and Path("/dev/shm").is_dir()
432
+ # ▲▲▲ 変更点 ▲▲▲
433
 
434
  custom_css = """
435
  .audio-output-row { display: flex !important; flex-wrap: wrap !important; gap: 10px !important; }
 
445
  with gr.Blocks(css=custom_css) as app:
446
  MAX_AUDIO_OUTPUTS = 4
447
  ITEMS_PER_ROW = 4
448
+ # ▼▼▼ 変更点: キープの最大アイテム数を8に変更 ▼▼▼
449
  MAX_WORKBENCH_ITEMS = 8
450
  # ▲▲▲ 変更点 ▲▲▲
451
 
 
456
  # saved_audio_hashes_state = gr.State(set())
457
  # ▲▲▲ 変更点 ▲▲▲
458
 
459
+ # --- キープUI更新ヘルパー ---
460
  def update_workbench_ui(workbench_list: List[Dict]) -> Tuple:
461
  updates = []
462
  for i in range(MAX_WORKBENCH_ITEMS):
 
521
  type="filepath", interactive=False
522
  ))
523
  with gr.Row():
524
+ # ▼▼▼ 変更点: 保存ボタンを削除し、キープボタンの幅を広げる ▼▼▼
525
  # save_buttons.append(gr.Button("💾 保存", scale=1))
526
+ to_workbench_buttons.append(gr.Button("🛠️ キープに追加", scale=2))
527
  # ▲▲▲ 変更点 ▲▲▲
528
  audio_item_columns.append(audio_col)
529
 
 
544
  status_textbox = gr.Textbox(interactive=False, lines=5, max_lines=5, autoscroll=True, show_label=False, placeholder="ここにログが表示されます...")
545
 
546
  with gr.Column(scale=1):
547
+ # ▼▼▼ 変更点: 「融☆合モデルを使う」チェックボックスを削除し、レイアウトを調整 ▼▼▼
548
  with gr.Row():
549
+ selected_model_dropdown = gr.Dropdown(label="話者", choices=[], value=None, interactive=True, scale=4)
550
  refresh_model_list_button = gr.Button("再読込", scale=1)
551
+ # ▲▲▲ 変更点 ▲▲▲
552
+
553
  # ▼▼▼ 変更点: モデルファイル選択ドロップダウンを削除 ▼▼▼
554
  # selected_model_file_dropdown = gr.Dropdown(label="モデルファイル (.safetensors)", choices=[], value=None, interactive=True)
555
  # ▲▲▲ 変更点 ▲▲▲
 
579
  random_text_mode_slider = gr.Slider(label="分割の単位", minimum=1, maximum=4, value=1, step=1, info="1:形態素, 2:チャンク, 3:文節, 4:節", interactive=True)
580
  random_text_ratio_textbox = gr.Textbox(label="カタカナ化の割合", value="0.5, 1", info="カンマ区切りで複数指定可。指定値からランダムに1つ使用。", interactive=True)
581
 
582
+ # ▼▼▼ 変更点 2: キープのレイアウトを2列に変更 ▼▼▼
583
+ with gr.Tab("キープ"):
584
+ gr.Markdown("## キープ\n読み上げタブで生成した音声をここにストックし、結合や保存ができます。最大8個まで保持できます。")
585
 
586
  workbench_items = []
587
  all_workbench_ui_components = []
 
612
 
613
  with gr.Row():
614
  merge_preview_button = gr.Button("1.結合&プレビュー", variant="primary")
615
+ add_merged_to_workbench_button = gr.Button("2.キープに追加", variant="primary")
616
  delete_originals_checkbox = gr.Checkbox(label="結合時に自動で元ファイルを削除", value=False, interactive=True)
617
 
618
  preview_audio_player = gr.Audio(label="結合結果プレビュー", interactive=False, type="filepath")
619
 
620
+ # ▼▼▼ 変更点: キープの保存機能を削除 ▼▼▼
621
  # gr.Markdown("---")
622
  #
623
  # with gr.Blocks():
624
+ # gr.Markdown("#### キープの音声を保存")
625
  # with gr.Row():
626
  # with gr.Column(scale=1):
627
  # audio_to_save_num_input = gr.Number(label="保存する音声の番号", value=1, minimum=1, step=1, precision=0, interactive=True)
 
687
  default_weight = styles_map[first_key].get("weight", DEFAULT_STYLE_WEIGHT)
688
  return gr.update(choices=display_names, value=default_display_name), gr.update(value=default_weight), styles_map
689
 
690
+ # ▼▼▼ 変更点: action_refresh_model_list関数からシンボリックリンク関連のロジックを削除 ▼▼▼
691
+ def action_refresh_model_list():
692
  """モデルリストを再読み込みし、UIとバックエンドの状態を同期させる。"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
693
  # バックエンドのリストを更新
694
  model_holder.refresh()
695
+
696
+ # UIのドロップダウンを更新
697
+ # マージモデルのパースとソートは維持する
698
+ ui_model_list = model_holder.model_names
699
+ formatted_choices = format_and_sort_model_names(ui_model_list)
700
+ value = formatted_choices[0][1] if formatted_choices else None
701
+ model_dropdown_update = gr.update(choices=formatted_choices, value=value)
 
 
 
 
 
702
 
703
  style_dropdown_update, style_weight_update, styles_data_state_update = load_styles_for_ui(value)
704
 
705
  return model_dropdown_update, style_dropdown_update, style_weight_update, styles_data_state_update
706
+ # ▲▲▲ 変更点 ▲▲▲
707
 
708
  def on_model_select_change(selected_model_name: Optional[str]):
709
  # ▼▼▼ 変更点: モデルファイル選択UIがなくなったため、関連処理を削除 ▼▼▼
 
897
 
898
  return tuple(final_outputs)
899
 
900
+ # --- キープイベントハンドラ ---
901
  def add_to_workbench(
902
  current_status: str,
903
  current_workbench_list: List[Dict],
 
907
  safe_workbench_list = current_workbench_list or []
908
 
909
  if not audio_path or not Path(audio_path).exists():
910
+ log_messages.append("⚠️ [キープ追加エラー] 追加する音声ファイルが見つかりません。")
911
  final_status = (current_status + "\n" + "\n".join(log_messages)).strip()
912
  return (final_status, safe_workbench_list) + update_workbench_ui(safe_workbench_list)
913
 
914
  if any(item['audio_path'] == audio_path for item in safe_workbench_list):
915
+ log_messages.append("ℹ️ この音声はすでにキープに存在します。")
916
  final_status = (current_status + "\n" + "\n".join(log_messages)).strip()
917
  return (final_status, safe_workbench_list) + update_workbench_ui(safe_workbench_list)
918
 
 
941
  path_to_delete.unlink()
942
  except Exception as e:
943
  print(f"Warning: Failed to delete old workbench audio file: {e}")
944
+ log_messages.append(f"ℹ️ キープのアイテムが最大数({MAX_WORKBENCH_ITEMS})に達したため、一番古いアイテムを削除しました。")
945
 
946
  ui_updates = update_workbench_ui(updated_list)
947
+ log_messages.append("✅ キープに音声を追加しました。")
948
  final_status = (current_status + "\n" + "\n".join(log_messages)).strip()
949
  return (final_status, updated_list) + ui_updates
950
 
 
959
  path_to_delete = Path(item_to_remove['audio_path'])
960
  if path_to_delete.exists() and str(path_to_delete.parent) == tempfile.gettempdir():
961
  path_to_delete.unlink()
962
+ log_messages.append(f"✅ キープからアイテム #{index_to_remove + 1} を削除し、一時ファイル��クリーンアップしました。")
963
  elif path_to_delete.exists():
964
+ log_messages.append(f"✅ キープからアイテム #{index_to_remove + 1} を削除しました。(ファイルは保持: {path_to_delete.name})")
965
  else:
966
+ log_messages.append(f"✅ キープからアイテム #{index_to_remove + 1} を削除しました。(関連ファイルなし)")
967
  except Exception as e:
968
+ log_messages.append(f"⚠️ キープのアイテム #{index_to_remove + 1} のファイル削除中にエラー: {e}")
969
 
970
  updated_list = [item for i, item in enumerate(safe_workbench_list) if i != index_to_remove]
971
  ui_updates = update_workbench_ui(updated_list)
 
980
  ):
981
  log_messages = []
982
  if not workbench_list:
983
+ log_messages.append("⚠️ [結合プレビュー警告] キープに音声がありません。")
984
  final_status = (current_status + "\n" + "\n".join(log_messages)).strip()
985
  return final_status, None, {}
986
 
 
1080
  log_messages = []
1081
  safe_workbench_list = current_workbench_list or []
1082
  if not preview_data or "audio_path" not in preview_data:
1083
+ log_messages.append("⚠️ [キープ追加エラー] 追加する結合済み音声がありません。先にプレビューを生成してください。")
1084
  final_status = (current_status + "\n" + "\n".join(log_messages)).strip()
1085
  return (final_status, safe_workbench_list) + update_workbench_ui(safe_workbench_list)
1086
 
1087
  src_path = Path(preview_data["audio_path"])
1088
  if not src_path.exists():
1089
+ log_messages.append("⚠️ [キープ追加エラー] 結合済み音声ファイルが見つかりません。")
1090
  final_status = (current_status + "\n" + "\n".join(log_messages)).strip()
1091
  return (final_status, safe_workbench_list) + update_workbench_ui(safe_workbench_list)
1092
 
 
1118
  log_messages.append(f"⚠️ 元の音声ファイル削除中にエラー: {e}")
1119
 
1120
  final_workbench_list = [new_merged_item] + remaining_list
1121
+ log_messages.append(f"✅ 結合音声をキープに追加し、元の音声(#{idx1+1}, #{idx2+1})を削除しました。")
1122
  else:
1123
  final_workbench_list = [new_merged_item] + safe_workbench_list
1124
+ log_messages.append("✅ 結合済みの音声をキープの一番上に追加しました。")
1125
 
1126
  if len(final_workbench_list) > MAX_WORKBENCH_ITEMS:
1127
  item_to_remove = final_workbench_list.pop(-1)
 
1131
  path_to_delete.unlink()
1132
  except Exception as e:
1133
  print(f"Warning: Failed to delete old workbench audio file: {e}")
1134
+ log_messages.append(f"ℹ️ キープが最大数({MAX_WORKBENCH_ITEMS})に達したため一番古いアイテムを削除しました。")
1135
 
1136
  ui_updates = update_workbench_ui(final_workbench_list)
1137
  final_status = (current_status + "\n" + "\n".join(log_messages)).strip()
 
1140
 
1141
  # --- イベントリスナー接続 ---
1142
  # ▼▼▼ 変更点: イベントリスナーの inputs/outputs を修正 ▼▼▼
1143
+ refresh_model_list_button.click(
1144
+ action_refresh_model_list,
1145
+ inputs=[],
1146
+ outputs=[selected_model_dropdown, current_styles_dropdown, style_weight_for_synth_slider, all_styles_data_state]
1147
+ )
1148
 
1149
  selected_model_dropdown.change(on_model_select_change,
1150
  inputs=[selected_model_dropdown],
 
1163
  fn=action_run_synthesis,
1164
  inputs=[
1165
  selected_model_dropdown, # selected_model_file_dropdown は削除
1166
+ # use_symlink_mode_checkbox は削除
1167
  current_styles_dropdown, style_weight_for_synth_slider,
1168
  text_input, generation_mode_radio, batch_count_slider,
1169
  language_dropdown, seed_input, speaker_name_textbox,
 
1209
  outputs=[status_textbox, preview_audio_player, merged_preview_state]
1210
  )
1211
 
1212
+ # ▼▼▼ 変更点: キープの保存ボタンのクリックイベントを削除 ▼▼▼
1213
  # save_creative_button.click(...)
1214
  # ▲▲▲ 変更点 ▲▲▲
1215
 
 
1228
 
1229
  player_width_slider.release(lambda w: f"<script>document.documentElement.style.setProperty('--audio-width', '{w}px');</script>", inputs=[player_width_slider], outputs=[js_injector_html])
1230
 
1231
+ app.load(
1232
+ action_refresh_model_list,
1233
+ inputs=[],
1234
+ outputs=[selected_model_dropdown, current_styles_dropdown, style_weight_for_synth_slider, all_styles_data_state]
1235
+ )
1236
  # ▲▲▲ 変更点 ▲▲▲
1237
  return app
1238
 
 
1240
  if __name__ == "__main__":
1241
  if Path("model_assets").exists(): shutil.rmtree("model_assets")
1242
 
1243
+ # ▼▼▼ 変更点: shm_path関連のコードを削除 ▼▼▼
1244
+ # shm_path = Path("/dev/shm")
1245
+ # ▲▲▲ 変更点 ▲▲▲
1246
 
1247
  mock_model_holder = TTSModelHolder()
1248
  print(f"Initial models loaded by TTSModelHolder: {mock_model_holder.model_names}")
 
1251
  assets_dir_path = assets_root_path.resolve()
1252
  assets_dir_path.mkdir(exist_ok=True)
1253
  allowed_paths = [str(assets_dir_path)]
1254
+
1255
+ # ▼▼▼ 変更点: shm_pathをallowed_pathsに追加するロジックを削除 ▼▼▼
1256
+ # if sys.platform != "win32" and shm_path.exists():
1257
+ # allowed_paths.append(str(shm_path.resolve()))
1258
+ # ▲▲▲ 変更点 ▲▲▲
1259
 
1260
  output_dir_path = Path("output").resolve()
1261
  (output_dir_path / "normal").mkdir(exist_ok=True, parents=True)