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 |