aka7774 commited on
Commit
68f523b
·
verified ·
1 Parent(s): 42ece8d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +141 -97
app.py CHANGED
@@ -35,8 +35,8 @@ def stream_process_output(process, log_list):
35
  log_list.append(line.strip()) # 余分な改行を削除
36
  if process.stderr:
37
  for line in iter(process.stderr.readline, ''):
38
- # WARNING以外のstderrはエラーとして強調表示しても良い
39
  processed_line = f"stderr: {line.strip()}"
 
40
  if "warning" not in line.lower():
41
  processed_line = f"ERROR (stderr): {line.strip()}"
42
  log_list.append(processed_line)
@@ -44,96 +44,130 @@ def stream_process_output(process, log_list):
44
  log_list.append(f"Error reading process stream: {e}")
45
 
46
  # --- Gradio アプリのバックエンド関数 ---
47
- def convert_safetensors_to_onnx_gradio(safetensors_file_obj, progress=gr.Progress(track_tqdm=True)):
 
 
 
 
48
  """
49
- アップロードされたSafetensorsモデルをONNXに変換し、結果をダウンロード可能にする。
 
50
  """
51
  log = ["Starting ONNX conversion..."]
52
  # 初期状態ではダウンロードファイルは空
53
  yield "\n".join(log), None
54
 
 
55
  if safetensors_file_obj is None:
56
- log.append("Error: No safetensors file uploaded. Please upload a .safetensors file.")
57
- # エラーメッセージを表示し、ダウンロードはNoneのまま
 
 
 
 
 
 
 
58
  yield "\n".join(log), None
59
  return
60
 
61
- # Style-Bert-VITS2 パスの確認
62
  add_sbv2_to_path()
63
  if not SBV2_REPO_PATH.exists():
64
- log.append(f"Error: Style-Bert-VITS2 repository not found at {SBV2_REPO_PATH}. Check Space build logs.")
65
  yield "\n".join(log), None
66
  return
67
 
68
- # 出力ディレクトリ作成 (存在しない場合)
69
  OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
70
 
71
- # 以前の出力ファイルを削除 (オプション、ディスクスペース節約のため)
72
- # for item in OUTPUT_DIR.glob("*.onnx"):
73
- # try:
74
- # item.unlink()
75
- # log.append(f"Cleaned up previous output: {item.name}")
76
- # except OSError as e:
77
- # log.append(f"Warning: Could not delete previous file {item.name}: {e}")
78
-
79
  onnx_output_path_str = None # 最終的なONNXファイルパス (文字列)
80
  current_log = log[:] # ログリストをコピー
81
 
82
  try:
83
- # 一時ディレクトリを作成して処理
84
  with tempfile.TemporaryDirectory() as temp_dir:
85
  temp_dir_path = Path(temp_dir)
86
- # Gradio Fileコンポーネントは一時ファイルパスを .name で持つ
87
- original_filename = Path(safetensors_file_obj.name).name
88
- # ファイル名に不正な文字が含まれていないか基本的なチェック (オプション)
89
- if "/" in original_filename or "\\" in original_filename or ".." in original_filename:
90
- current_log.append(f"Error: Invalid characters found in filename: {original_filename}")
 
 
 
91
  yield "\n".join(current_log), None
92
  return
 
93
 
94
- temp_safetensors_path_in_root = temp_dir_path / original_filename
95
-
96
- current_log.append(f"Processing uploaded file: {original_filename}")
97
- current_log.append(f"Copying to temporary location: {temp_safetensors_path_in_root}")
98
- yield "\n".join(current_log), None
99
- # アップロードされたファイルオブジェクトから一時ディレクトリにコピー
100
- shutil.copy(safetensors_file_obj.name, temp_safetensors_path_in_root)
101
-
102
-
103
- # --- SBV2が期待するディレクトリ構造を一時ディレクトリ内に作成 ---
104
- # モデル名をファイル名から取得 (拡張子なし)
105
- model_name = temp_safetensors_path_in_root.stem
106
  # assets_root を一時ディレクトリ自体にする
107
  assets_root = temp_dir_path
108
  # assets_root の下に model_name のディレクトリを作成
109
  model_dir_in_temp = assets_root / model_name
110
  model_dir_in_temp.mkdir(exist_ok=True)
111
- # safetensorsファイルを model_name ディレクトリに移動
112
- temp_safetensors_path = model_dir_in_temp / original_filename
113
- shutil.move(temp_safetensors_path_in_root, temp_safetensors_path)
114
-
115
- # dataset_root も assets_root と同じにしておく (今回は使用しない)
116
- dataset_root = assets_root
117
 
118
- current_log.append(f"Using temporary assets_root: {assets_root}")
119
- current_log.append(f"Created temporary model directory: {model_dir_in_temp}")
120
- current_log.append(f"Using temporary model path: {temp_safetensors_path}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
  yield "\n".join(current_log), None
122
 
123
  # --- paths.yml を一時的に設定 ---
124
  config_path = SBV2_REPO_PATH / "configs" / "paths.yml"
125
  config_path.parent.mkdir(parents=True, exist_ok=True)
126
- paths_config = {"dataset_root": str(dataset_root.resolve()), "assets_root": str(assets_root.resolve())}
 
127
  with open(config_path, "w", encoding="utf-8") as f:
128
  yaml.dump(paths_config, f)
129
- current_log.append(f"Saved temporary paths config to {config_path}")
130
  yield "\n".join(current_log), None
131
 
132
  # --- ONNX変換スクリプト実行 ---
133
- current_log.append(f"\nStarting ONNX conversion script for: {temp_safetensors_path.name}")
134
  convert_script = SBV2_REPO_PATH / "convert_onnx.py"
135
  if not convert_script.exists():
136
- current_log.append(f"Error: convert_onnx.py not found at '{convert_script}'.")
137
  yield "\n".join(current_log), None
138
  return # tryブロックを抜ける
139
 
@@ -142,40 +176,35 @@ def convert_safetensors_to_onnx_gradio(safetensors_file_obj, progress=gr.Progres
142
  python_executable,
143
  str(convert_script.resolve()),
144
  "--model",
145
- str(temp_safetensors_path.resolve()) # 一時ディレクトリ内のモデルパス
146
  ]
147
- current_log.append(f"\nRunning command: {' '.join(command)}")
148
  yield "\n".join(current_log), None
149
 
150
  process_env = os.environ.copy()
151
- # メモリリーク対策? (あまり効果ないかも)
152
- # process_env["PYTORCH_CUDA_ALLOC_CONF"] = "max_split_size_mb:128"
153
-
154
  process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
155
  text=True, encoding='utf-8', errors='replace',
156
  cwd=SBV2_REPO_PATH, # スクリプトの場所で実行
157
  env=process_env)
158
 
159
  # ログ出力用リスト (スレッドと共有)
160
- process_output_lines = []
161
  thread = threading.Thread(target=stream_process_output, args=(process, process_output_lines))
162
  thread.start()
163
 
164
  # 進捗表示のためのループ
165
  while thread.is_alive():
166
- # yieldでGradio UIを更新 (現在のログ + プロセスからのログ)
167
  yield "\n".join(current_log + process_output_lines), None
168
- time.sleep(0.2) # 更新頻度
169
 
170
- # スレッド終了待ち
171
  thread.join()
172
- # プロセス終了待ち (タイムアウト設定)
173
  try:
174
- process.wait(timeout=900) # 15分タイムアウト
175
  except subprocess.TimeoutExpired:
176
  current_log.extend(process_output_lines) # ここまでのログを追加
177
- current_log.append("\nError: Conversion process timed out after 15 minutes.")
178
- process.kill() # タイムアウトしたらプロセスを強制終了
179
  yield "\n".join(current_log), None
180
  return # tryブロックを抜ける
181
 
@@ -187,102 +216,119 @@ def convert_safetensors_to_onnx_gradio(safetensors_file_obj, progress=gr.Progres
187
  processed_stderr = []
188
  for line in final_stderr.strip().split('\n'):
189
  processed_line = f"stderr: {line.strip()}"
190
- if "warning" not in line.lower():
191
  processed_line = f"ERROR (stderr): {line.strip()}"
192
  processed_stderr.append(processed_line)
193
- if processed_stderr:
194
- process_output_lines.append("--- stderr ---")
195
  process_output_lines.extend(processed_stderr)
196
- process_output_lines.append("--------------")
 
 
 
 
 
197
 
198
  # 全てのプロセスログをメインログに追加
199
  current_log.extend(process_output_lines)
 
200
  current_log.append("\n-------------------------------")
201
 
202
  # --- 結果の確認と出力ファイルのコピー ---
203
  if process.returncode == 0:
204
- current_log.append("ONNX conversion command finished successfully.")
205
  # 期待されるONNXファイルパス (入力と同じディレクトリ内)
206
  expected_onnx_path_in_temp = temp_safetensors_path.with_suffix(".onnx")
207
 
208
  if expected_onnx_path_in_temp.exists():
209
- current_log.append(f"Found converted ONNX file: {expected_onnx_path_in_temp.name}")
210
  # 一時ディレクトリから永続的な出力ディレクトリにコピー
211
  final_onnx_path = OUTPUT_DIR / expected_onnx_path_in_temp.name
212
- shutil.copy(expected_onnx_path_in_temp, final_onnx_path)
213
- current_log.append(f"Copied ONNX file for download to: {final_onnx_path}")
214
- onnx_output_path_str = str(final_onnx_path) # ダウンロード用ファイルパスを設定
 
 
 
215
  else:
216
- current_log.append(f"Warning: Expected ONNX file not found at '{expected_onnx_path_in_temp}'. Please check the logs.")
217
  else:
218
- current_log.append(f"ONNX conversion command failed with return code {process.returncode}.")
219
- current_log.append("Please check the logs above for errors (especially lines starting with 'ERROR').")
220
 
221
- # 一時ディレクトリが自動で削除される前に yield する必要がある
222
  yield "\n".join(current_log), onnx_output_path_str
223
 
224
  except FileNotFoundError as e:
225
- # コマンドが見つからない場合など
226
- current_log.append(f"\nError: A required command or file was not found: {e.filename}. Check Dockerfile setup and PATH.")
227
  current_log.append(f"{e}")
228
  yield "\n".join(current_log), None
229
  except Exception as e:
230
- current_log.append(f"\nAn unexpected error occurred: {e}")
231
  import traceback
232
  current_log.append(traceback.format_exc())
233
- # エラー発生時も最終ログとNoneを返す
234
  yield "\n".join(current_log), None
235
  finally:
236
- # ガベージコレクションを試みる (メモリ解放目的)
237
  gc.collect()
238
- # 最終的な状態をUIに反映させるための最後のyield (重要)
239
- # tryブロック内で既にyieldしている場合でも、finallyで再度yieldすることで
240
- # UIが最終状態(エラーメッセージや成功メッセージ+ダウンロードリンク)に更新される
241
  print("Conversion function finished.") # サーバーログ用
242
- # ここで再度yieldするとUIの更新が確実になることがある
243
- # yield "\n".join(current_log), onnx_output_path_str
244
 
245
 
246
  # --- Gradio Interface ---
247
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
248
  gr.Markdown("# Style-Bert-VITS2 Safetensors to ONNX Converter")
249
  gr.Markdown(
250
- "Upload your `.safetensors` model file, convert it to ONNX format, and download the result. "
251
- "The environment setup (cloning repo, installing dependencies, running initialize.py) "
252
- "is handled automatically when this Space starts."
 
 
253
  )
254
 
255
  with gr.Row():
256
  with gr.Column(scale=1):
 
257
  safetensors_upload = gr.File(
258
- label="1. Upload Safetensors Model",
259
  file_types=[".safetensors"],
260
- # file_count="single" (default)
261
  )
262
- convert_button = gr.Button("2. Convert to ONNX", variant="primary")
 
 
 
 
 
 
 
 
 
 
263
  gr.Markdown("---")
 
264
  onnx_download = gr.File(
265
- label="3. Download ONNX Model",
266
  interactive=False, # 出力専用
267
  )
268
  gr.Markdown(
269
- "**Note:** Conversion can take several minutes, especially on free hardware. "
270
- "Please be patient. The log on the right will update during the process."
271
  )
272
 
273
  with gr.Column(scale=2):
274
  output_log = gr.Textbox(
275
  label="Conversion Log",
276
- lines=25, # 少し高さを増やす
277
  interactive=False,
278
  autoscroll=True,
279
- max_lines=1500 # ログが多くなる可能性を考慮
280
  )
281
 
282
  # ボタンクリック時のアクション設定
283
  convert_button.click(
284
  convert_safetensors_to_onnx_gradio,
285
- inputs=[safetensors_upload],
286
  outputs=[output_log, onnx_download] # ログとダウンロードファイルの2つを出力
287
  )
288
 
@@ -295,6 +341,4 @@ if __name__ == "__main__":
295
  print(f"Output directory: {OUTPUT_DIR.resolve()}")
296
 
297
  # Gradioアプリを起動
298
- # share=True にするとパブリックリンクが生成される(HF Spacesでは不要)
299
- # queue() を使うと複数ユーザーのリクエストを処理しやすくなる
300
  demo.queue().launch()
 
35
  log_list.append(line.strip()) # 余分な改行を削除
36
  if process.stderr:
37
  for line in iter(process.stderr.readline, ''):
 
38
  processed_line = f"stderr: {line.strip()}"
39
+ # 警告はそのまま、他はエラーとして強調 (任意)
40
  if "warning" not in line.lower():
41
  processed_line = f"ERROR (stderr): {line.strip()}"
42
  log_list.append(processed_line)
 
44
  log_list.append(f"Error reading process stream: {e}")
45
 
46
  # --- Gradio アプリのバックエンド関数 ---
47
+ def convert_safetensors_to_onnx_gradio(
48
+ safetensors_file_obj,
49
+ config_file_obj,
50
+ style_vectors_file_obj
51
+ ): # gr.Progress は削除
52
  """
53
+ アップロードされたSafetensors, config.json, style_vectors.npy を使って
54
+ ONNXに変換し、結果をダウンロード可能にする。
55
  """
56
  log = ["Starting ONNX conversion..."]
57
  # 初期状態ではダウンロードファイルは空
58
  yield "\n".join(log), None
59
 
60
+ # --- ファイルアップロードの検証 ---
61
  if safetensors_file_obj is None:
62
+ log.append("Error: Safetensors file is missing. Please upload the .safetensors file.")
63
+ yield "\n".join(log), None
64
+ return
65
+ if config_file_obj is None:
66
+ log.append("❌ Error: config.json file is missing. Please upload the config.json file.")
67
+ yield "\n".join(log), None
68
+ return
69
+ if style_vectors_file_obj is None:
70
+ log.append("❌ Error: style_vectors.npy file is missing. Please upload the style_vectors.npy file.")
71
  yield "\n".join(log), None
72
  return
73
 
74
+ # --- Style-Bert-VITS2 パスの確認 ---
75
  add_sbv2_to_path()
76
  if not SBV2_REPO_PATH.exists():
77
+ log.append(f"Error: Style-Bert-VITS2 repository not found at {SBV2_REPO_PATH}. Check Space build logs.")
78
  yield "\n".join(log), None
79
  return
80
 
81
+ # --- 出力ディレクトリ作成 ---
82
  OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
83
 
 
 
 
 
 
 
 
 
84
  onnx_output_path_str = None # 最終的なONNXファイルパス (文字列)
85
  current_log = log[:] # ログリストをコピー
86
 
87
  try:
88
+ # --- 一時ディレクトリを作成して処理 ---
89
  with tempfile.TemporaryDirectory() as temp_dir:
90
  temp_dir_path = Path(temp_dir)
91
+ current_log.append(f"📁 Created temporary directory: {temp_dir_path}")
92
+ yield "\n".join(current_log), None # UI更新
93
+
94
+ # --- SBV2が期待するディレクトリ構造を一時ディレクトリ内に作成 ---
95
+ # モデル名を .safetensors ファイル名から取得 (拡張子なし)
96
+ safetensors_filename = Path(safetensors_file_obj.name).name
97
+ if not safetensors_filename.lower().endswith(".safetensors"):
98
+ current_log.append(f"❌ Error: Invalid safetensors filename: {safetensors_filename}")
99
  yield "\n".join(current_log), None
100
  return
101
+ model_name = Path(safetensors_filename).stem # 拡張子を除いた部分
102
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  # assets_root を一時ディレクトリ自体にする
104
  assets_root = temp_dir_path
105
  # assets_root の下に model_name のディレクトリを作成
106
  model_dir_in_temp = assets_root / model_name
107
  model_dir_in_temp.mkdir(exist_ok=True)
108
+ current_log.append(f" - Created model directory: {model_dir_in_temp.relative_to(assets_root)}")
109
+ yield "\n".join(current_log), None
 
 
 
 
110
 
111
+ # --- 3つのファイルを model_dir_in_temp にコピー ---
112
+ files_to_copy = {
113
+ "safetensors": safetensors_file_obj,
114
+ "config.json": config_file_obj,
115
+ "style_vectors.npy": style_vectors_file_obj,
116
+ }
117
+ copied_paths = {}
118
+
119
+ for file_key, file_obj in files_to_copy.items():
120
+ original_filename = Path(file_obj.name).name
121
+ # ファイル名の基本的な検証 (サニタイズはより厳密に行うことも可能)
122
+ if "/" in original_filename or "\\" in original_filename or ".." in original_filename:
123
+ current_log.append(f"❌ Error: Invalid characters found in filename: {original_filename}")
124
+ yield "\n".join(current_log), None
125
+ return # tryブロックを抜ける
126
+ # 期待されるファイル名と一致しているか確認 (config と style_vectors)
127
+ if file_key == "config.json" and original_filename.lower() != "config.json":
128
+ current_log.append(f"⚠️ Warning: Uploaded JSON file name is '{original_filename}', expected 'config.json'. Using uploaded name.")
129
+ if file_key == "style_vectors.npy" and original_filename.lower() != "style_vectors.npy":
130
+ current_log.append(f"⚠️ Warning: Uploaded NPY file name is '{original_filename}', expected 'style_vectors.npy'. Using uploaded name.")
131
+
132
+ destination_path = model_dir_in_temp / original_filename
133
+ try:
134
+ shutil.copy(file_obj.name, destination_path)
135
+ current_log.append(f" - Copied '{original_filename}' to model directory.")
136
+ # .safetensorsファイルのパスを保存しておく
137
+ if file_key == "safetensors":
138
+ copied_paths["safetensors"] = destination_path
139
+ except Exception as e:
140
+ current_log.append(f"❌ Error copying file '{original_filename}': {e}")
141
+ yield "\n".join(current_log), None
142
+ return # tryブロックを抜ける
143
+ yield "\n".join(current_log), None # 各ファイルコピー後にUI更新
144
+
145
+ # safetensorsファイルがコピーされたか確認
146
+ temp_safetensors_path = copied_paths.get("safetensors")
147
+ if not temp_safetensors_path:
148
+ current_log.append("❌ Error: Failed to locate the copied safetensors file in the temporary directory.")
149
+ yield "\n".join(current_log), None
150
+ return
151
+
152
+ current_log.append(f"✅ All required files copied to temporary model directory.")
153
+ current_log.append(f" - Using temporary assets_root: {assets_root}")
154
  yield "\n".join(current_log), None
155
 
156
  # --- paths.yml を一時的に設定 ---
157
  config_path = SBV2_REPO_PATH / "configs" / "paths.yml"
158
  config_path.parent.mkdir(parents=True, exist_ok=True)
159
+ # dataset_root は今回は使わないが設定はしておく (assets_rootと同じ場所)
160
+ paths_config = {"dataset_root": str(assets_root.resolve()), "assets_root": str(assets_root.resolve())}
161
  with open(config_path, "w", encoding="utf-8") as f:
162
  yaml.dump(paths_config, f)
163
+ current_log.append(f" - Saved temporary paths config to {config_path}")
164
  yield "\n".join(current_log), None
165
 
166
  # --- ONNX変換スクリプト実行 ---
167
+ current_log.append(f"\n🚀 Starting ONNX conversion script for model '{model_name}'...")
168
  convert_script = SBV2_REPO_PATH / "convert_onnx.py"
169
  if not convert_script.exists():
170
+ current_log.append(f"Error: convert_onnx.py not found at '{convert_script}'. Check repository setup.")
171
  yield "\n".join(current_log), None
172
  return # tryブロックを抜ける
173
 
 
176
  python_executable,
177
  str(convert_script.resolve()),
178
  "--model",
179
+ str(temp_safetensors_path.resolve()) # 一時ディレクトリ内の .safetensors パス
180
  ]
181
+ current_log.append(f"\n Running command: {' '.join(command)}")
182
  yield "\n".join(current_log), None
183
 
184
  process_env = os.environ.copy()
 
 
 
185
  process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
186
  text=True, encoding='utf-8', errors='replace',
187
  cwd=SBV2_REPO_PATH, # スクリプトの場所で実行
188
  env=process_env)
189
 
190
  # ログ出力用リスト (スレッドと共有)
191
+ process_output_lines = ["\n--- Conversion Script Output ---"]
192
  thread = threading.Thread(target=stream_process_output, args=(process, process_output_lines))
193
  thread.start()
194
 
195
  # 進捗表示のためのループ
196
  while thread.is_alive():
 
197
  yield "\n".join(current_log + process_output_lines), None
198
+ time.sleep(0.3) # 更新頻度
199
 
200
+ # スレッド終了待ちとプロセス終了待ち
201
  thread.join()
 
202
  try:
203
+ process.wait(timeout=12000) # 20分タイムアウト (モデルサイズにより調整)
204
  except subprocess.TimeoutExpired:
205
  current_log.extend(process_output_lines) # ここまでのログを追加
206
+ current_log.append("\n❌ Error: Conversion process timed out after 20 minutes.")
207
+ process.kill()
208
  yield "\n".join(current_log), None
209
  return # tryブロックを抜ける
210
 
 
216
  processed_stderr = []
217
  for line in final_stderr.strip().split('\n'):
218
  processed_line = f"stderr: {line.strip()}"
219
+ if "warning" not in line.lower() and line.strip(): # 空行と警告以外
220
  processed_line = f"ERROR (stderr): {line.strip()}"
221
  processed_stderr.append(processed_line)
222
+ if any(line.startswith("ERROR") for line in processed_stderr):
223
+ process_output_lines.append("--- Errors/Warnings (stderr) ---")
224
  process_output_lines.extend(processed_stderr)
225
+ process_output_lines.append("-----------------------------")
226
+ elif processed_stderr: # 警告のみの場合
227
+ process_output_lines.append("--- Warnings (stderr) ---")
228
+ process_output_lines.extend(processed_stderr)
229
+ process_output_lines.append("------------------------")
230
+
231
 
232
  # 全てのプロセスログをメインログに追加
233
  current_log.extend(process_output_lines)
234
+ current_log.append("--- End Script Output ---")
235
  current_log.append("\n-------------------------------")
236
 
237
  # --- 結果の確認と出力ファイルのコピー ---
238
  if process.returncode == 0:
239
+ current_log.append("ONNX conversion command finished successfully.")
240
  # 期待されるONNXファイルパス (入力と同じディレクトリ内)
241
  expected_onnx_path_in_temp = temp_safetensors_path.with_suffix(".onnx")
242
 
243
  if expected_onnx_path_in_temp.exists():
244
+ current_log.append(f" - Found converted ONNX file: {expected_onnx_path_in_temp.name}")
245
  # 一時ディレクトリから永続的な出力ディレクトリにコピー
246
  final_onnx_path = OUTPUT_DIR / expected_onnx_path_in_temp.name
247
+ try:
248
+ shutil.copy(expected_onnx_path_in_temp, final_onnx_path)
249
+ current_log.append(f" - Copied ONNX file for download to: {final_onnx_path.relative_to(SCRIPT_DIR)}")
250
+ onnx_output_path_str = str(final_onnx_path) # ダウンロード用ファイルパスを設定
251
+ except Exception as e:
252
+ current_log.append(f"❌ Error copying ONNX file to output directory: {e}")
253
  else:
254
+ current_log.append(f"⚠️ Warning: Expected ONNX file not found at '{expected_onnx_path_in_temp.name}'. Check script output above.")
255
  else:
256
+ current_log.append(f"ONNX conversion command failed with return code {process.returncode}.")
257
+ current_log.append(" Please check the logs above for errors (especially lines starting with 'ERROR').")
258
 
259
+ # 一時ディレクトリが自動で削除される前に最終結果をyield
260
  yield "\n".join(current_log), onnx_output_path_str
261
 
262
  except FileNotFoundError as e:
263
+ current_log.append(f"\n❌ Error: A required command or file was not found: {e.filename}. Check Dockerfile setup and PATH.")
 
264
  current_log.append(f"{e}")
265
  yield "\n".join(current_log), None
266
  except Exception as e:
267
+ current_log.append(f"\n❌ An unexpected error occurred: {e}")
268
  import traceback
269
  current_log.append(traceback.format_exc())
 
270
  yield "\n".join(current_log), None
271
  finally:
272
+ # ガベージコレクション
273
  gc.collect()
 
 
 
274
  print("Conversion function finished.") # サーバーログ用
275
+ # 最後のyieldUIを最終状態に更新
276
+ # yield "\n".join(current_log), onnx_output_path_str # tryブロック内で既に返している
277
 
278
 
279
  # --- Gradio Interface ---
280
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
281
  gr.Markdown("# Style-Bert-VITS2 Safetensors to ONNX Converter")
282
  gr.Markdown(
283
+ "Upload your model's `.safetensors`, `config.json`, and `style_vectors.npy` files. "
284
+ "The application will convert the model to ONNX format, and you can download the resulting `.onnx` file."
285
+ )
286
+ gr.Markdown(
287
+ "_(Environment setup is handled automatically when this Space starts.)_"
288
  )
289
 
290
  with gr.Row():
291
  with gr.Column(scale=1):
292
+ gr.Markdown("### 1. Upload Model Files")
293
  safetensors_upload = gr.File(
294
+ label="Safetensors Model (.safetensors)",
295
  file_types=[".safetensors"],
 
296
  )
297
+ config_upload = gr.File(
298
+ label="Config File (config.json)",
299
+ file_types=[".json"],
300
+ )
301
+ style_vectors_upload = gr.File(
302
+ label="Style Vectors (style_vectors.npy)",
303
+ file_types=[".npy"],
304
+ )
305
+
306
+ convert_button = gr.Button("2. Convert to ONNX", variant="primary", elem_id="convert_button")
307
+
308
  gr.Markdown("---")
309
+ gr.Markdown("### 3. Download Result")
310
  onnx_download = gr.File(
311
+ label="ONNX Model (.onnx)",
312
  interactive=False, # 出力専用
313
  )
314
  gr.Markdown(
315
+ "**Note:** Conversion can take **several minutes** (5-20+ min depending on model size and hardware). "
316
+ "Please be patient. The log on the right shows the progress."
317
  )
318
 
319
  with gr.Column(scale=2):
320
  output_log = gr.Textbox(
321
  label="Conversion Log",
322
+ lines=30, # 高さをさらに増やす
323
  interactive=False,
324
  autoscroll=True,
325
+ max_lines=2000 # ログが増える可能性
326
  )
327
 
328
  # ボタンクリック時のアクション設定
329
  convert_button.click(
330
  convert_safetensors_to_onnx_gradio,
331
+ inputs=[safetensors_upload, config_upload, style_vectors_upload],
332
  outputs=[output_log, onnx_download] # ログとダウンロードファイルの2つを出力
333
  )
334
 
 
341
  print(f"Output directory: {OUTPUT_DIR.resolve()}")
342
 
343
  # Gradioアプリを起動
 
 
344
  demo.queue().launch()