File size: 8,280 Bytes
5b07cee
 
 
 
 
 
8beb92c
f173379
5b07cee
 
3731d5a
5b07cee
 
8beb92c
5b07cee
ccc5133
5b07cee
 
f173379
5b07cee
 
 
f173379
 
 
 
 
 
 
 
 
5b07cee
f173379
 
 
5b07cee
f173379
 
 
 
 
 
 
 
 
 
 
5b07cee
f173379
 
 
 
 
 
 
 
 
 
 
8beb92c
f173379
 
 
5b07cee
f173379
 
 
 
 
 
5b07cee
f173379
5b07cee
f173379
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8beb92c
f173379
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8beb92c
f173379
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5b07cee
f173379
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
import pandas as pd
import zipfile
import base64
import os
import requests
import re
from pydub import AudioSegment
import json

def remove_quotes(text):
    # ダブルクォートを空文字に置き換え
    return text.replace('"', '')

def text_to_speech(input_file, selected_option):
    # APIキーを直接コードに埋め込む(実際の運用では推奨されません)
    api_key = 'AIzaSyBqvy0wRyZVapYyZ_GfTlRpQwrwj9YqYaw'  # ここを実際のAPIキーに置き換えてください
    data = pd.read_csv(input_file)
    zip_path = 'output_audio_files.zip'
    error_log = []  # エラーログを格納するリスト

    with zipfile.ZipFile(zip_path, 'w') as z:
        for idx, row in data.iterrows():
            try:
                # script列が文字列か確認し、文字列でない場合は空文字に置き換える
                script = row.get('script', '')
                print("原稿", script)
                if pd.isna(script):
                    error_log.append(f"行 {idx+1}: スクリプトが空です")
                    continue
                if not isinstance(script, str):
                    script = str(script)
                    
                # テキストをA:やB:で分割
                parts = re.split(r'(A:|B:)', script)
                ssml_parts = []

                # 交互に発言するAとBの内容を順に処理
                for i in range(1, len(parts), 2):
                    if parts[i] == "A:":
                        voice_name = row["voiceA"]
                    elif parts[i] == "B:":
                        voice_name = row["voiceB"]
                    else:
                        continue  # A:またはB:で始まらない行は無視
                        
                    text = parts[i + 1].strip()
                    text = remove_quotes(text)

                    # 1sに変換する前に除外するコード
                    text = text.replace("a.m.", 'AM')
                    text = text.replace("p.m.", 'PM')
                    text = text.replace("U.S.", 'US')
                    text = text.replace("U.K.", 'UK')
                    text = text.replace("Mr.", 'Mister')
                    text = text.replace("Ms.", 'MIZ')
                    text = text.replace("Mrs.", 'Misiz')
                    text = text.replace("Dr.", 'Doctor')
                    text = text.replace("Mt.", 'Mount')
                    text = text.replace("Prof.", 'Professor')

                    # テキスト内の改行を1sの間に変換
                    text = text.replace("\n", '<break time="1s"/>')
                    text = text.replace(".", '.<break time="500ms"/>')

                    if selected_option == "ブレイクタイム有":
                        # 「,」で時間を空ける
                        if row["eikenn"] in ["5級", "4級", "3級", "準2級", "2級", "準1級"]:
                            text = text.replace(",", '<break time="50ms"/>')
                    
                    ssml_parts.append(f'<voice name="{voice_name}"><prosody rate="{row["speed"]}"><p>{text}</p></prosody></voice>')

                ssml = '<speak>' + ''.join(ssml_parts)
                
                if pd.notna(row.get('question')) and row['question'] != '':
                    ssml += f'<break time="1s"/><voice name="{row["voiceQuestion"]}"><prosody rate="{row["speed"]}"><p>Question</p><break time="1s"/><p>{row["question"]}</p></prosody></voice>'
                if pd.notna(row.get('choices')) and row['choices'] != '':
                    choices_list = row['choices'].split('/')
                    choices_ssml = '<break time="1s"/>'.join(choices_list)
                    ssml += f'<break time="1s"/><voice name="{row["voiceB"]}"><prosody rate="{row["speed"]}"><p>{choices_ssml}</p></prosody></voice>'
                ssml += '</speak>'

                # APIリクエスト用のボディ
                body = {
                    "input": {"ssml": ssml},
                    "voice": {"languageCode": "en-US"},  # 基本的な言語設定(必要に応じて行ごとに変更可能)
                    "audioConfig": {"audioEncoding": "MP3"}
                }
                headers = {
                    "X-Goog-Api-Key": api_key,
                    "Content-Type": "application/json"
                }
                url = "https://texttospeech.googleapis.com/v1/text:synthesize"
                
                # APIリクエスト送信とレスポンスの確認
                try:
                    response = requests.post(url, headers=headers, json=body)
                    
                    # ステータスコードの確認
                    if response.status_code != 200:
                        error_message = f"行 {idx+1}: APIエラー - HTTP {response.status_code}"
                        try:
                            error_data = response.json()
                            if 'error' in error_data and 'message' in error_data['error']:
                                error_message += f" - {error_data['error']['message']}"
                        except:
                            error_message += f" - レスポンス: {response.text}"
                        
                        print(error_message)
                        error_log.append(error_message)
                        continue
                        
                    response_data = response.json()
                    
                    # 音声コンテンツが存在するか確認
                    if 'audioContent' not in response_data:
                        error_message = f"行 {idx+1}: 音声データがレスポンスに含まれていません"
                        print(error_message)
                        error_log.append(error_message)
                        continue
                        
                    # 音声コンテンツの取得とファイル保存
                    audio_content = base64.b64decode(response_data['audioContent'])
                    temp_file_name = f"{row['id']}_temp.mp3"
                    final_file_name = f"{row['id']}.mp3"
                    
                    # 一時ファイルに保存
                    with open(temp_file_name, "wb") as out:
                        out.write(audio_content)
                    
                    # pydubを使用して128kbpsで再保存
                    audio = AudioSegment.from_file(temp_file_name, format="mp3")
                    audio.export(final_file_name, format="mp3", bitrate="128k")
                    
                    # ZIPファイルに追加
                    z.write(final_file_name)
                    
                    # 一時ファイルを削除
                    os.remove(temp_file_name)
                    os.remove(final_file_name)
                    
                    print(f"行 {idx+1}: 処理成功 - ファイルID: {row['id']}")
                    
                except requests.exceptions.RequestException as e:
                    error_message = f"行 {idx+1}: API接続エラー - {str(e)}"
                    print(error_message)
                    error_log.append(error_message)
                
            except Exception as e:
                error_message = f"行 {idx+1}: 予期せぬエラー - {str(e)}"
                print(error_message)
                error_log.append(error_message)

    # エラーログのサマリーを出力
    if error_log:
        print("\n===== エラーログ =====")
        for error in error_log:
            print(error)
        print(f"合計 {len(error_log)} 件のエラーが発生しました。")
        
        # エラーログをファイルに保存
        with open('tts_error_log.txt', 'w', encoding='utf-8') as log_file:
            log_file.write("===== Google Text-to-Speech API エラーログ =====\n")
            log_file.write(f"処理日時: {pd.Timestamp.now()}\n\n")
            for error in error_log:
                log_file.write(f"{error}\n")
        print("エラーログは 'tts_error_log.txt' に保存されました。")
    else:
        print("すべての行が正常に処理されました。")

    return zip_path