1
+ #!/usr/bin/env python
2
+ # filepath: /home/runner/work/template-fastapi/template-fastapi/scripts/speeches.py
3
+
4
+ import json
5
+ import time
6
+ from typing import List
7
+
8
+ import typer
9
+ from rich .console import Console
10
+ from rich .table import Table
11
+ from rich .progress import Progress , SpinnerColumn , TextColumn
12
+
13
+ from template_fastapi .models .speech import BatchTranscriptionRequest , TranscriptionStatus
14
+ from template_fastapi .repositories .speeches import SpeechRepository
15
+
16
+ app = typer .Typer ()
17
+ console = Console ()
18
+ speech_repo = SpeechRepository ()
19
+
20
+
21
+ @app .command ()
22
+ def create_transcription (
23
+ content_urls : List [str ] = typer .Argument (..., help = "転写するファイルのURL(複数指定可能)" ),
24
+ locale : str = typer .Option ("ja-JP" , "--locale" , "-l" , help = "言語設定" ),
25
+ display_name : str = typer .Option (None , "--name" , "-n" , help = "転写ジョブの表示名" ),
26
+ model : str = typer .Option (None , "--model" , "-m" , help = "使用するモデル" ),
27
+ ):
28
+ """新しい転写ジョブを作成する"""
29
+ console .print (f"[bold green]転写ジョブを作成します[/bold green]" )
30
+ console .print (f"ファイルURL: { ', ' .join (content_urls )} " )
31
+ console .print (f"言語設定: { locale } " )
32
+
33
+ try :
34
+ request = BatchTranscriptionRequest (
35
+ content_urls = content_urls ,
36
+ locale = locale ,
37
+ display_name = display_name or "CLI Batch Transcription" ,
38
+ model = model
39
+ )
40
+
41
+ response = speech_repo .create_transcription_job (request )
42
+
43
+ console .print (f"✅ [bold green]転写ジョブが正常に作成されました[/bold green]" )
44
+ console .print (f"ジョブID: { response .job_id } " )
45
+ console .print (f"ステータス: { response .status .value } " )
46
+
47
+ if response .message :
48
+ console .print (f"メッセージ: { response .message } " )
49
+
50
+ except Exception as e :
51
+ console .print (f"❌ [bold red]エラー[/bold red]: { str (e )} " )
52
+
53
+
54
+ @app .command ()
55
+ def get_transcription (
56
+ job_id : str = typer .Argument (..., help = "転写ジョブID" ),
57
+ ):
58
+ """転写ジョブの状態を取得する"""
59
+ console .print (f"[bold green]転写ジョブの状態を取得します[/bold green]" )
60
+ console .print (f"ジョブID: { job_id } " )
61
+
62
+ try :
63
+ job = speech_repo .get_transcription_job (job_id )
64
+
65
+ console .print (f"\n [bold blue]転写ジョブ情報[/bold blue]:" )
66
+ console .print (f"ID: { job .id } " )
67
+ console .print (f"名前: { job .name } " )
68
+ console .print (f"ステータス: { job .status .value } " )
69
+ console .print (f"作成日時: { job .created_date_time } " )
70
+ console .print (f"最終更新日時: { job .last_action_date_time } " )
71
+
72
+ if job .links :
73
+ console .print (f"リンク: { json .dumps (job .links , indent = 2 , ensure_ascii = False )} " )
74
+
75
+ except Exception as e :
76
+ console .print (f"❌ [bold red]エラー[/bold red]: { str (e )} " )
77
+
78
+
79
+ @app .command ()
80
+ def get_transcription_files (
81
+ job_id : str = typer .Argument (..., help = "転写ジョブID" ),
82
+ ):
83
+ """転写ジョブのファイル一覧を取得する"""
84
+ console .print (f"[bold green]転写ファイル一覧を取得します[/bold green]" )
85
+ console .print (f"ジョブID: { job_id } " )
86
+
87
+ try :
88
+ files = speech_repo .get_transcription_files (job_id )
89
+
90
+ if not files :
91
+ console .print ("[yellow]転写ファイルが見つかりませんでした[/yellow]" )
92
+ return
93
+
94
+ # テーブルで表示
95
+ table = Table (title = "転写ファイル一覧" )
96
+ table .add_column ("名前" , style = "cyan" )
97
+ table .add_column ("種類" , style = "green" )
98
+ table .add_column ("リンク" , style = "yellow" )
99
+
100
+ for file in files :
101
+ table .add_row (
102
+ file .get ("name" , "N/A" ),
103
+ file .get ("kind" , "N/A" ),
104
+ file .get ("links" , {}).get ("contentUrl" , "N/A" )
105
+ )
106
+
107
+ console .print (table )
108
+ console .print (f"[bold blue]合計: { len (files )} 件[/bold blue]" )
109
+
110
+ except Exception as e :
111
+ console .print (f"❌ [bold red]エラー[/bold red]: { str (e )} " )
112
+
113
+
114
+ @app .command ()
115
+ def get_transcription_result (
116
+ file_url : str = typer .Argument (..., help = "転写結果ファイルのURL" ),
117
+ save_file : str = typer .Option (None , "--save" , "-s" , help = "結果を保存するファイル名" ),
118
+ ):
119
+ """転写結果を取得する"""
120
+ console .print (f"[bold green]転写結果を取得します[/bold green]" )
121
+ console .print (f"ファイルURL: { file_url } " )
122
+
123
+ try :
124
+ result = speech_repo .get_transcription_result (file_url )
125
+
126
+ console .print (f"\n [bold blue]転写結果[/bold blue]:" )
127
+ console .print (f"ソース: { result .source } " )
128
+ console .print (f"タイムスタンプ: { result .timestamp } " )
129
+ console .print (f"継続時間: { result .duration_in_ticks } " )
130
+
131
+ if result .combined_recognized_phrases :
132
+ console .print (f"\n [bold yellow]統合認識フレーズ[/bold yellow]:" )
133
+ for phrase in result .combined_recognized_phrases :
134
+ console .print (f"- { phrase .get ('display' , 'N/A' )} " )
135
+
136
+ if result .recognized_phrases :
137
+ console .print (f"\n [bold yellow]認識フレーズ({ len (result .recognized_phrases )} 件)[/bold yellow]:" )
138
+ for i , phrase in enumerate (result .recognized_phrases [:5 ]): # 最初の5件のみ表示
139
+ console .print (f"{ i + 1 } . { phrase .get ('display' , 'N/A' )} " )
140
+
141
+ if len (result .recognized_phrases ) > 5 :
142
+ console .print (f"... および { len (result .recognized_phrases ) - 5 } 件の追加フレーズ" )
143
+
144
+ # ファイルに保存
145
+ if save_file :
146
+ with open (save_file , 'w' , encoding = 'utf-8' ) as f :
147
+ json .dump (result .dict (), f , ensure_ascii = False , indent = 2 , default = str )
148
+ console .print (f"✅ 結果を { save_file } に保存しました" )
149
+
150
+ except Exception as e :
151
+ console .print (f"❌ [bold red]エラー[/bold red]: { str (e )} " )
152
+
153
+
154
+ @app .command ()
155
+ def delete_transcription (
156
+ job_id : str = typer .Argument (..., help = "転写ジョブID" ),
157
+ force : bool = typer .Option (False , "--force" , "-f" , help = "確認なしで削除" ),
158
+ ):
159
+ """転写ジョブを削除する"""
160
+ console .print (f"[bold yellow]転写ジョブを削除します[/bold yellow]" )
161
+ console .print (f"ジョブID: { job_id } " )
162
+
163
+ if not force :
164
+ confirm = typer .confirm ("本当に削除しますか?" )
165
+ if not confirm :
166
+ console .print ("削除をキャンセルしました" )
167
+ return
168
+
169
+ try :
170
+ success = speech_repo .delete_transcription_job (job_id )
171
+
172
+ if success :
173
+ console .print (f"✅ [bold green]転写ジョブ '{ job_id } ' を正常に削除しました[/bold green]" )
174
+ else :
175
+ console .print (f"❌ [bold red]転写ジョブの削除に失敗しました[/bold red]" )
176
+
177
+ except Exception as e :
178
+ console .print (f"❌ [bold red]エラー[/bold red]: { str (e )} " )
179
+
180
+
181
+ @app .command ()
182
+ def list_transcriptions ():
183
+ """転写ジョブの一覧を取得する"""
184
+ console .print ("[bold green]転写ジョブ一覧を取得します[/bold green]" )
185
+
186
+ try :
187
+ jobs = speech_repo .list_transcription_jobs ()
188
+
189
+ if not jobs :
190
+ console .print ("[yellow]転写ジョブが見つかりませんでした[/yellow]" )
191
+ return
192
+
193
+ # テーブルで表示
194
+ table = Table (title = "転写ジョブ一覧" )
195
+ table .add_column ("ID" , style = "cyan" )
196
+ table .add_column ("名前" , style = "green" )
197
+ table .add_column ("ステータス" , style = "yellow" )
198
+ table .add_column ("作成日時" , style = "magenta" )
199
+ table .add_column ("最終更新日時" , style = "blue" )
200
+
201
+ for job in jobs :
202
+ table .add_row (
203
+ job .id ,
204
+ job .name or "N/A" ,
205
+ job .status .value ,
206
+ str (job .created_date_time ) if job .created_date_time else "N/A" ,
207
+ str (job .last_action_date_time ) if job .last_action_date_time else "N/A"
208
+ )
209
+
210
+ console .print (table )
211
+ console .print (f"[bold blue]合計: { len (jobs )} 件[/bold blue]" )
212
+
213
+ except Exception as e :
214
+ console .print (f"❌ [bold red]エラー[/bold red]: { str (e )} " )
215
+
216
+
217
+ @app .command ()
218
+ def wait_for_completion (
219
+ job_id : str = typer .Argument (..., help = "転写ジョブID" ),
220
+ timeout : int = typer .Option (300 , "--timeout" , "-t" , help = "タイムアウト時間(秒)" ),
221
+ interval : int = typer .Option (10 , "--interval" , "-i" , help = "チェック間隔(秒)" ),
222
+ ):
223
+ """転写ジョブの完了を待つ"""
224
+ console .print (f"[bold green]転写ジョブの完了を待ちます[/bold green]" )
225
+ console .print (f"ジョブID: { job_id } " )
226
+ console .print (f"タイムアウト: { timeout } 秒" )
227
+ console .print (f"チェック間隔: { interval } 秒" )
228
+
229
+ start_time = time .time ()
230
+
231
+ with Progress (
232
+ SpinnerColumn (),
233
+ TextColumn ("[progress.description]{task.description}" ),
234
+ transient = True ,
235
+ ) as progress :
236
+ task = progress .add_task (description = "転写処理中..." , total = None )
237
+
238
+ while time .time () - start_time < timeout :
239
+ try :
240
+ job = speech_repo .get_transcription_job (job_id )
241
+
242
+ if job .status == TranscriptionStatus .SUCCEEDED :
243
+ progress .update (task , description = "✅ 転写が完了しました" )
244
+ console .print (f"✅ [bold green]転写ジョブが正常に完了しました[/bold green]" )
245
+ console .print (f"ジョブID: { job .id } " )
246
+ console .print (f"最終更新日時: { job .last_action_date_time } " )
247
+ return
248
+ elif job .status == TranscriptionStatus .FAILED :
249
+ progress .update (task , description = "❌ 転写が失敗しました" )
250
+ console .print (f"❌ [bold red]転写ジョブが失敗しました[/bold red]" )
251
+ console .print (f"ジョブID: { job .id } " )
252
+ return
253
+ elif job .status == TranscriptionStatus .RUNNING :
254
+ progress .update (task , description = "🔄 転写処理中..." )
255
+ else :
256
+ progress .update (task , description = f"⏳ 待機中 ({ job .status .value } )" )
257
+
258
+ time .sleep (interval )
259
+
260
+ except Exception as e :
261
+ progress .update (task , description = f"❌ エラー: { str (e )} " )
262
+ console .print (f"❌ [bold red]エラー[/bold red]: { str (e )} " )
263
+ return
264
+
265
+ # タイムアウト
266
+ console .print (f"⏰ [bold yellow]タイムアウトしました({ timeout } 秒)[/bold yellow]" )
267
+ console .print ("転写ジョブはまだ処理中の可能性があります" )
268
+
269
+
270
+ if __name__ == "__main__" :
271
+ app ()
0 commit comments