"""ElevenLabs の Voice Design で、登場人物の声の候補を作り、気に入った候補を声として保存する。エイイチの Mac で動かす。
準備: ターミナルで  read -s ELEVENLABS_API_KEY && export ELEVENLABS_API_KEY

使い方（_制作ベース のフォルダで）
  候補を作る:  python3 voice_design.py design 大城      → 声の候補/大城/候補1.mp3〜候補3.mp3 を聞き比べる
               python3 voice_design.py design 大城 --seed 12   （気に入るものがなければ seed を変えてもう一度）
  保存する:    python3 voice_design.py save 大城 2       → 候補2 を声にして、声の一覧.json に Voice ID を書く
人物の説明と試しの文は、下の CAST に書く（英語の説明の方が安定する）
"""
import argparse, base64, json, os, sys, urllib.request, urllib.error

CAST = {
    "大城": dict(
        desc="A Japanese woman in her late twenties, a kind and cheerful hospital nurse. Clear, warm, natural "
             "conversational Japanese with a medium pitch, friendly and a little brisk. Native Japanese speaker with "
             "natural standard Tokyo Japanese intonation, speaking Japanese. Not anime-like, not breathy. Studio quality recording.",
        text="空き箱、ぶつけないようにね。今日は申し送りが多いから、先にこっちの確認からいこう。ハコブくん、"
             "三階のナースステーションまでお願いね。ありがとう、助かったよ。次の患者さんの点滴、準備しておくね。それじゃあ、よろしくお願いします。"),
    "寺島": dict(
        desc="A Japanese woman in her mid forties, a seasoned, down-to-earth hospital nurse. Warm, slightly low pitch, "
             "practical and a little blunt but kind, natural everyday Japanese. Native Japanese speaker with "
             "natural standard Tokyo Japanese intonation, speaking Japanese. Not anime-like, not breathy. Studio quality recording.",
        text="この箱、運ぶの手伝って。え、呼んでないよ？ ちょっと、そこ通るよ。まったくもう、慌てなくていいからね。"
             "あとで倉庫の在庫も見ておいてくれる？ 足りないものがあったら、私に言ってちょうだい。じゃあ、お願いね。"),
    "白石": dict(
        desc="A Japanese woman in her late twenties, a serious young robotics engineer. Polite, earnest and a little "
             "nervous, clear medium pitch, natural conversational Japanese. Native Japanese speaker with natural "
             "standard Tokyo Japanese intonation, speaking Japanese. Not anime-like, not breathy. Studio quality recording.",
        text="今月から、ハコブくんの評価指標は配送件数です。えっと、ログを見ると、件数は先月の十倍です。"
             "ただ、ちょっと気になる点もあって。すみません、もう一度データを確認してから、改めてご報告しますね。よろしくお願いします。"),
    "槙原": dict(
        desc="A Japanese man in his late twenties, a calm and polite robotics engineer. Soft, clear, slightly low "
             "medium pitch, careful and earnest, natural conversational Japanese. Native Japanese speaker with natural "
             "standard Tokyo Japanese intonation, speaking Japanese. Not anime-like, not breathy. Studio quality recording.",
        text="センサーの窓だけは、塞がないでください。飾り付けは大歓迎です。ただ、前を見るための窓がここにあるので、"
             "この丸い部分だけは空けておいてもらえると助かります。目印のシールを貼っておきますね。どうぞ、よろしくお願いします。"),
    "田中": dict(
        desc="A Japanese man in his mid thirties, a busy hospital doctor. Brisk, confident, slightly impatient but "
             "kind, medium-low pitch, natural conversational Japanese. Native Japanese speaker with natural standard "
             "Tokyo Japanese intonation, speaking Japanese. Not anime-like, not breathy. Studio quality recording.",
        text="三〇三号室まで、急ぎで頼む。わかったから、早く。確認はもういいよ。次の患者さんが待ってるんだ。"
             "ああ、ごめん、さっきの検査結果、あとで病棟にも回しておいてくれるかな。よろしく。それじゃあ、また夕方の回診で。"),
    "小芝": dict(
        desc="A Japanese man in his mid twenties who has only just become a doctor after residency and has no confidence yet. "
             "Timid, hesitant and unsure of himself, soft and slightly thin voice, speaks a little quietly with small pauses "
             "as if checking whether he is right, light medium-high pitch, gentle and awkward but sincere. Natural conversational "
             "Japanese. Native Japanese speaker with natural standard Tokyo Japanese intonation, speaking Japanese. "
             "Not anime-like, not breathy, not a deep voice. Studio quality recording.",
        text="あの、三〇一号室に、ラシックスを……お願いします。あれ、さっきも言いましたっけ。すみません、えっと、"
             "ちょっと自信がなくて。大城さん、移動可能な経路の中で、という条件定義が……必要だった、かな。次からは、ちゃんと確認します。"),
    "高杉": dict(
        desc="A Japanese woman in her early thirties, a calm and polite hospital nurse. Gentle, clear, medium-high pitch, "
             "natural conversational Japanese. Native Japanese speaker with natural standard Tokyo Japanese intonation, speaking Japanese. Not anime-like, not breathy. Studio quality recording.",
        text="はい、今行きます。こちらのお部屋で大丈夫ですよ。ゆっくりで構いませんからね。何かあったら、"
             "このボタンで呼んでください。夜中でも、すぐに来ますから。お大事になさってください。明日の朝も、また様子を見に来ますね。"),
    "中村": dict(   # 2026-10-24 追加（入院患者の中村拓也さん・第72話・第84話。名前の出ない男性の患者にも使う）
        desc="A Japanese man in his early forties, an inpatient at a hospital. Friendly, relaxed and a little cheeky, "
             "medium-low pitch, casual everyday Japanese, a bit playful. Native Japanese speaker with natural standard "
             "Tokyo Japanese intonation, speaking Japanese. Not anime-like, not breathy. Studio quality recording.",
        text="ちょっと、トイレどこ？ いやあ、置いてかれるかと思ったよ。先生、俺より緊張してません？ "
             "声まね、けっこう自信あるんですよ。プリン、まだかなあ。入院してると、楽しみがそれくらいしかなくてね。よろしく頼みますよ。"),
    "森田茂": dict(   # 2026-10-24 追加（年配の男性。第65話の面会の人。名前の出ない年配の男性にも使う）
        desc="A Japanese man in his late sixties, a gentle and polite retired man visiting a hospital. Soft, slightly hoarse, "
             "low pitch, calm but a little unsure and hesitant, natural conversational Japanese. Native Japanese speaker with "
             "natural standard Tokyo Japanese intonation, speaking Japanese. Not anime-like, not breathy. Studio quality recording.",
        text="すいません、三〇四号室は、どっちですか。家内の見舞いに来たんですが、どうも道がわからなくて。"
             "この病院は広いですなあ。ああ、ありがとうございます。助かりました。エレベーターは、あちらでよろしいですか。お世話になります。"),
    "森田久美子": dict(   # 2026-10-24 追加（年配の女性。第71話の面会の久美子さん）
        desc="A Japanese woman in her mid sixties, a warm, talkative and frank housewife. Medium pitch, lively, a little brisk "
             "like a cheerful neighborhood aunt, natural everyday Japanese. Native Japanese speaker with natural standard "
             "Tokyo Japanese intonation, speaking Japanese. Not anime-like, not breathy. Studio quality recording.",
        text="あら、ロボットさんは？ 速いのよ、あなた。こっちは荷物持ってるんだから。そう、それでいいの。"
             "ゆっくりでいいのよ、ゆっくりで。うちの人の病室、もうすぐかしら。ありがとうね、ほんとに助かったわ。また来週も来るからね。"),
    "ハコブ_イケボ": dict(   # 2026-10-24 追加（第17話。アップデートでなった「超重低音のイケメンボイス」のハコブくん）
        desc="A Japanese man with an extremely deep, rich bass voice, very handsome and theatrical, like a loyal samurai retainer "
             "in a period drama. Slow, dignified, smooth and a little over-the-top, very low pitch. Native Japanese speaker "
             "with natural standard Tokyo Japanese intonation, speaking Japanese. Not anime-like, not breathy. Studio quality recording.",
        text="御意。ただいま参上つかまつった、姫君。お申し付けとあらば、この身が朽ちるまで、どこへなりとも運びましょう。"
             "……御意。姫君の、望むままに。拙者、検体の搬送にかけては、誰にも引けを取りませぬ。いざ、参らん。"),
}

ap = argparse.ArgumentParser()
ap.add_argument("mode", choices=["design", "save"])
ap.add_argument("who")
ap.add_argument("pick", nargs="?", type=int)
ap.add_argument("--seed", type=int, default=7)
ap.add_argument("--model", default="eleven_ttv_v3")   # 2026-10-19: 既定（multilingual_ttv_v2）では日本語の抑揚が不自然だったので v3 にした
a = ap.parse_args()
key = os.environ.get("ELEVENLABS_API_KEY")
if not key:
    sys.exit("ELEVENLABS_API_KEY が入っていません。read -s ELEVENLABS_API_KEY && export ELEVENLABS_API_KEY を先に")
if a.who not in CAST:
    sys.exit(f"CAST にいない名前です: {a.who}（いるのは {'・'.join(CAST)}）")
c = CAST[a.who]
d = os.path.join("声の候補", a.who)
os.makedirs(d, exist_ok=True)

def post(url, body):
    req = urllib.request.Request(url, data=json.dumps(body).encode(), headers={"xi-api-key": key, "Content-Type": "application/json"})
    try:
        with urllib.request.urlopen(req, timeout=180) as r:
            return json.loads(r.read())
    except urllib.error.HTTPError as e:
        sys.exit(f"止まりました: {e.code} {e.read().decode(errors='ignore')[:400]}")

if a.mode == "design":
    r = post("https://api.elevenlabs.io/v1/text-to-voice/design?output_format=mp3_44100_128",
             {"voice_description": c["desc"], "text": c["text"], "seed": a.seed, "model_id": a.model})
    ids = []
    for k, p in enumerate(r["previews"], 1):
        open(os.path.join(d, f"候補{k}.mp3"), "wb").write(base64.b64decode(p["audio_base_64"]))
        ids.append(p["generated_voice_id"])
    json.dump({"seed": a.seed, "ids": ids}, open(os.path.join(d, "候補.json"), "w"), ensure_ascii=False, indent=1)
    print(f"{a.who} の候補を {len(ids)} つ作りました: {d}/候補1.mp3〜  聞いて、気に入った番号で  python3 voice_design.py save {a.who} 番号")
else:
    if not a.pick:
        sys.exit("保存する候補の番号を書いてください（例: python3 voice_design.py save 大城 2）")
    ids = json.load(open(os.path.join(d, "候補.json")))["ids"]
    r = post("https://api.elevenlabs.io/v1/text-to-voice",
             {"voice_name": f"P2M_{a.who}", "voice_description": c["desc"], "generated_voice_id": ids[a.pick - 1]})
    book = json.load(open("声の一覧.json")) if os.path.exists("声の一覧.json") else {}
    book[a.who] = r["voice_id"]
    json.dump(book, open("声の一覧.json", "w"), ensure_ascii=False, indent=1)
    print(f"{a.who} の声を保存しました: {r['voice_id']}（声の一覧.json に書きました）")
