# -*- coding: utf-8 -*-
# 台本「ハコブくんの声と絵は、どう作っているのか」— シリーズ「ひとりとAIのチーム」4回目（描く係・声の係）
#   2026-11-01 1版。筋はエイイチの決定（絵 → 声 → 読み・今日の失敗は「腕が3本」・候補の声を流す）
#   設計: _ネタ帳/シリーズ_ひとりとAIのチーム_20261018.md・骨組み: 台本_15本_20261012/25_ハコブくんの声と絵.md
#   書き写した元: 動画/AIに昨日の続きをさせる/narration.py（形・読みの直し・自己紹介とアウトロの定型）
#   数えた値（2026-11-01）:
#     - 読みの直しの表: 動画フォルダの narration.py 37本の ELEVEN_READ を重複なしで数えて207組（read_table）
#     - 過去の間違いの表: ハコブくん/4コママンガ作成データ/指示/チェック項目と過去の間違い.md の表 22行（2026-10-30〜31）（ledger）
#   出典（台本の中の事実）:
#     - hook_koma・fail_arm・fail_fix: チェック項目と過去の間違い.md（第121話 P3 高杉さんの腕が3本・第131話 P3 大城さんの腕が3本・見つけたのはエイイチ・直し ANATOMY CHECK）
#     - body・face: video_rules.md 4-7（体は ChatGPT で作り直した絵 cut_new.png（hakobu.py BODY="new"）・顔はコード・表情8・口4段階）
#     - manga・manga_flow・manga_why: 動画/_ネタ帳/マンガ_動画ネタ一覧.md（ep01 2025-12-21）・site_archive/index/02_マンガ一覧.md（日本語99話・2026-10-12 時点）・道具/vbuild.py（gemini-3.1-flash-image）・
#       チェック項目と過去の間違い.md「進め方」・マンガのプロジェクト指示_横長版（4コマを1枚で生成しない理由）
#     - size: チェック項目と過去の間違い.md B（原因1・yline と下地・measure）
#     - vd・vd_text・why・model: _制作ベース/ハコブくんの声_候補.md（説明文6つ・1つで3つ出る・候補1は読みは正確だが見た目に落ち着きすぎ・候補6パターン2に決定・v4 と Multilingual v2 を冒頭14文で比較）・video_rules.md 3-1（声の説明の言い方）
#     - cast: video_rules.md 9-0b（自作の声は今のプランで10個が上限・常連の声を借りる）・_制作ベース/声の一覧.json（槙原＝Sryf5x…・エイイチの声に使っている）
#     - read_ex・quiz_ans・try: video_rules.md 3-1（栄一・描く・係・節・球・--try と全文の違い）
#   2026-11-01 2版: 別のエージェントの照合（直す9・整える7）で直した（結論を「最後に選んで通すのは人」に・22行・99話・体は作り直した絵・球はハロの回・14文は1本目）
#   2026-11-01 3版: エイイチの決定で確定（codex なし）。候補の声は ID を忘れたので流さず、サイトのトップの前の声（ハコブくん/ハコブくん関連データ/ハコブくん_動画.mp4・2026-08-08・site_archive の top にも同じ10.04秒の動画）を流す（listen_old の後の間 8.6秒に src/前の声.m4a を sound.py の SE で入れる）
#   2026-11-01 4版（確認用1回目の指摘）: 何話→なんわ・エイイチの登場と声（ei_intro・EI_LINES 4文）・冒頭のコマを真ん中に・槙原と「表」の抑揚を --try で選ぶ
#   2026-11-01 5版: 「表」は全文で抑揚がそろわず、--try でエイイチが「リスト」への言い換えを選んだ（表情は除く）。「槙原」は ELEVEN_READ のひらがな（まきはら）をやめて漢字のまま（--try 01 をエイイチが選んだ）
#   エイイチの実感（2026-11-01 に聞いた答え。言い回しだけ整えた）: honne_check（気が抜けない）・honne_voice（何度か試した・最初は高いキャラ声）・mitate_fear・mitate_job
#   エイイチに確かめてほしい所: S079 は Claude の下書き
# 話すのはハコブくん。エイイチは三人称で呼ぶ（video_rules.md 3-1）
#
# 各要素: (場面の名前, 表情, 字幕の文, 後ろの間[秒])  間を省略すると 0.35 秒
FACES = ("base", "smile", "surprise", "doubt", "think", "trouble", "joy", "wink")

SEGS = [
 # 0 フック（0:00〜）
 ("hook_koma", "base", "このマンガのコマ、ちょっと、腕を数えてみてください。"),
 ("hook_koma", "surprise", "1、2、3。高杉さんの腕が、3本あります。"),
 ("hook_koma", "smile", "描いたのは、画像を作るAIです。見つけたのは、人の目でした。", 1.2),
 ("hook_voice", "base", "そして、いまお話ししている、ハコブくんのこの声も、AIで作っています。", 1.2),
 ("hook_q", "doubt", "AIの絵と声なら、ボタン1つで出てくるんじゃないの。そう思いませんか。"),
 ("hook_q", "smile", "今日は、ハコブくん自身の話です。ちょっと照れますが、作り方の中身を見ていきます。", 1.8),
 # 1 自己紹介
 ("title", "smile", "Pixels to Motors。案内役のハコブくんです。"),
 ("title", "base", "この番組では、フィジカルAIのニュースや実機の記録から、現場で使える形を考えます。", 1.2),
 ("title_eiichi", "base", "エイイチは、本業のデザイナーをしながら、ロボティクスの研究と開発をしています。｜その中で分かったことと、そこから考えた見立てを、ハコブくんがお伝えします。", 1.8),
 # 2 今日の問い
 ("question", "surprise", "今日の問いは、ハコブくんの声と絵は、どう作っているのか、です。", 1.2),
 ("series", "base", "AIを、チームの係のように使って、1人で研究と発信を続ける。その中身を見ていくシリーズの、4回目です。", 1.2),
 ("scope", "base", "見るのは、このチャンネルの作り方です。｜1人の、1つのやり方の話で、AIの製品の比べ合いではありません。", 1.8),
 # 3 結論と立場
 ("answer", "base", "先に結論を言うと、AIは候補を出します。AIも確かめますが、最後に選んで通すのは、人の目と耳です。"),
 ("answer", "think", "絵は、1コマずつAIに描かせて、決まりのリストで確かめ、最後に人が見て通します。｜声は、文章で頼んで候補を作り、人が聞き比べて決めました。読み間違いは、見つけるたびに、直しのリストに足しています。", 1.2),
 ("stance", "base", "先に言っておくと、AIに任せれば、絵も読みも自然に正しくなる、という話ではないんです。"),
 ("stance", "trouble", "人が選んで確かめるなら、手間は減っていないのでは。そう思った方もいるかもしれません。減った所と、減っていない所があります。それも見ていきます。", 1.8),
 # 4 目次
 ("toc", "base", "大きく3つあります。"),
 ("toc", "base", "絵を、どう描いているのか。"),
 ("toc", "base", "声を、どう選んだのか。"),
 ("toc", "base", "読み間違いを、どう直しているのか。"),
 ("toc", "base", "最後に、エイイチの見立てを話します。", 1.8),
 # 5 節1：絵を、どう描いているのか
 ("c1", "base", "1つ目、絵を、どう描いているのか、です。"),
 ("c1", "base", "一言で言うと、AIに1コマずつ描かせて、決まりのリストと人の目で、何度か確かめています。", 1.2),
 ("body", "base", "まず、画面の左にいる、このハコブくん。"),
 ("body", "base", "体は、画像を作るAIで作った絵から、切り抜いています。", 1.2),
 ("face", "smile", "顔は、コードで描いています。目と、口と、まばたき。表情は8つあります。｜口は、声の大きさに合わせて、4段階で開きます。", 1.2),
 ("manga", "base", "マンガのほうは、4コママンガ『ハコブくん』です。2025年12月に1話目を出して、10月半ばまでに、日本語版で99話を出しました。", 1.2),
 ("manga_flow", "base", "いまの作り方は、こうです。"),
 ("manga_flow", "base", "Claudeが、1コマずつ、絵の指示の文を書く。GeminiというAIが、その指示で絵を描く。"),
 ("manga_flow", "base", "描けたら、AIが、決まりのリストと見比べて、1コマずつ確かめる。何話もまとめて描いたときは、描いたのとは別の会話のAIに見てもらいます。最後に、人が全部のコマを見て、通すかを決めます。", 1.2),
 ("manga_why", "think", "4コマを1枚で描かせないのは、1枚に4つの構図を頼むと、どこかが崩れるからです。", 1.2),
 ("fail", "trouble", "今日の失敗は、冒頭の、腕が3本です。", 1.2),
 ("fail_arm", "base", "第121話の3コマ目で、高杉さんの腕が3本。第131話の3コマ目では、大城さんの腕が3本になっていました。どちらも、見つけたのは人でした。", 1.2),
 ("fail_fix", "base", "そこで、人が出るコマには、毎回、腕は2本、数えること、と指示に入れました。確かめるリストにも、腕と手と脚を、数える行があります。", 1.2),
 ("ledger", "base", "こうした間違いは、リストに1行ずつ足しています。11月1日に数えると、10月30日と31日の2日間で、22行ありました。"),
 ("ledger", "smile", "ハコブくんの横の輪に、穴が開いた。ハコブくんに、腕が生えた。扉が、頭の上に浮いた。"),
 ("ledger", "trouble", "……ハコブくんには、腕も脚もありません。生えたら困ります。", 1.2),
 ("size", "base", "いちばん手こずったのは、大きさです。"),
 ("size", "base", "身長を数字で書いても、絵のAIは、コマの空いた所に収まる大きさで描いてしまいます。｜そこで、頭のてっぺんと足もとの位置を線で決めた下地を、一緒に渡すようにしました。描いたあとに、高さの比も測ります。", 1.2),
 ("ei_intro", "smile", "ここで、作っている本人に聞いてみます。エイイチです。", 1.2),
 ("honne_check", "base", "声のイントネーションも、イラストの描き方の決まりの間違いも、まだ多いんだよね。気がつくと、デザインが変わってたりするから、気が抜けない。", 1.2),
 ("c1_sum", "think", "AIに描かせて、決まりのリストで確かめ、最後は人の目で通す。これが、1つ目です。", 1.8),
 # 6 節2：声を、どう選んだのか
 ("c2", "base", "2つ目、声を、どう選んだのか、です。"),
 ("c2", "base", "一言で言うと、文章で頼むと声を作る仕組みで候補を作り、人が聞き比べて決めました。", 1.2),
 ("vd", "base", "使ったのは、ElevenLabsのボイスデザインです。どんな声かを文章で書くと、新しい声を作ってくれます。", 1.2),
 ("vd_text", "base", "頼む文章は、6通り書きました。年代も、話し方も、少しずつ変えています。"),
 ("vd_text", "base", "1つの文章で、声が3つずつ出てきます。そこから、聞き比べていきました。", 1.2),
 ("why", "base", "最初の、落ち着いた声は、読み間違いもなく、正確でした。｜でも、ハコブくんの見た目には、落ち着きすぎていて、合わなかったんです。"),
 ("why", "think", "決まったのは、6つ目の文章で出た3つのうち、2つ目の声です。｜「説明好きで親切な、お手伝いロボット」をイメージした、30代くらいの男性の声を思わせる声です。誰かの声を録音して、元にしてはいません。", 1.2),
 ("model", "base", "声を作るモデルも、1本目の動画の冒頭の14文で聞き比べて、決めました。", 1.2),
 ("listen", "smile", "実は、ハコブくんの声は、これが最初ではありません。"),
 ("listen", "base", "声と絵は何度か試して、今の声に落ち着いたんだよ。最初は、もう少し高い、キャラクターっぽい声だった。", 1.2),
 ("listen_old", "base", "サイトのトップで流していた、ハコブくんの動画では、前は、こんな声でした。", 8.6),
 ("listen_now", "smile", "たしかに、高い、キャラクターらしい声ですね。いまの、この声は、ボイスデザインで作り直したものです。", 1.2),
 ("cast", "base", "マンガのショートでは、看護師さんや先生にも、同じ仕組みで声を付けています。"),
 ("cast", "trouble", "ただ、いまのプランでは、持てる声は10個までです。足りないので、同じ話に出ない人どうしで、声を貸し借りしています。"),
 ("cast", "smile", "謎解きの回で、右下に出てくるエイイチの声も、実はマンガの槙原くんの声を借りています。", 1.2),
 ("c2_sum", "think", "候補はAIが作る。決めるのは、聞いた人の耳。これが、2つ目です。", 1.8),
 # 7 節3：読み間違いを、どう直しているのか
 ("c3", "base", "3つ目、読み間違いを、どう直しているのか、です。"),
 ("c3", "base", "一言で言うと、間違いを見つけるたびに、直しのリストに足していきます。", 1.2),
 ("read_table", "base", "声を作る前に、台本の言葉を、読み方に置き換えるリストです。｜11月1日に、これまでの37本の台本の分を数えると、207組ありました。", 1.2),
 ("read_ex", "base", "たとえば、案内の最初に出てくる名前は、漢字にして渡しています。いくつかの書き方を聞き比べて、いちばん平らに読めたのが、漢字だったからです。"),
 ("read_ex", "trouble", "「描く」を「かく」と読んで、「書く」と聞き分けられなかったこともあります。いまは「えがく」と渡しています。"),
 ("read_ex", "base", "「係」は、漢字と、ひらがなと、カタカナで聞き比べて、カタカナにしました。", 1.2),
 ("quiz", "base", "ここで1つ、考えてみてください。｜同じ字で、読みが2つある言葉。AIは、どちらで読むかを、毎回同じに決めてくれるでしょうか。", 1.2),
 ("quiz_ans", "surprise", "答えは、同じとは限りません。"),
 ("quiz_ans", "base", "「節」という字を、同じ動画の中で、「せつ」と「ふし」に読み分けたことがありました。"),
 ("quiz_ans", "think", "そういう言葉は、読みを足すより、別の言葉に言い換えています。「節の頭」は、「章の頭」に、という具合です。"),
 ("quiz_ans", "smile", "ちなみに、1回目でお話しした、「球」を「きゅま」と読んだ失敗も、ハコブくんです。……ちゃんと覚えています。", 1.2),
 ("try", "base", "怪しい言葉は、書き方を変えて試し読みし、人が聞いて選びます。｜それでも、全文で読ませると、試しでは読めた言葉を、読み違えることがあります。だから、最後は通しで聞きます。", 1.2),
 ("less_more", "base", "では、手間は減ったのか。"),
 ("less_more", "base", "声を録音する、絵を一から描く、という作業は、していません。"),
 ("less_more", "think", "減っていないのは、選ぶことと、確かめることです。むしろ、確かめる所は、リストになって増えています。", 1.2),
 ("c3_sum", "think", "間違いを見つけたら、リストに足して、次から同じ所でつまずかないようにする。これが、3つ目です。", 1.8),
 # 8 見立て
 ("mitate", "base", "ここからは、エイイチの見立てです。", 1.2),
 ("mitate_fear", "base", "キャラクターの声と絵を、AIで作ることに、怖さはありますか。", 0.6),
 ("mitate_fear", "base", "怖いと思うことは、ないな。", 1.2),
 ("mitate_job", "think", "では、デザイナーとして、選ぶ仕事は、人に残るんでしょうか。", 0.6),
 ("mitate_job", "base", "これは、どうなんだろう。AIにはできないだろうと思っていた仕事が、あっという間に置き換わるようになってるから。選ぶ仕事も、なくなる可能性はあると思ってます。", 1.2),
 ("mitate_job", "think", "ただ、今日の記録を見る限り、腕が3本なのに気づいたのも、声の抑揚のおかしさに気づいたのも、人でした。", 1.8),
 # 9 問いへの回答・まとめ
 ("answer2", "base", "じゃあ、冒頭の問いに答えますね。ハコブくんの声と絵は、どう作っているのか。"),
 ("answer2", "think", "絵は、AIに1コマずつ描かせて、決まりのリストと人の目で確かめる。声は、文章で頼んで作った候補から、人が聞き比べて選ぶ。｜読み間違いは、見つけるたびに、リストに足していく。"),
 ("answer2", "base", "AIは候補を出し、AIも確かめる。それでも、最後に選んで通すのは、いまは人の目と耳です。", 1.2),
 ("question_next", "base", "AIの絵や声を見たら、聞いてみてください。その候補を、誰が選んで、誰が確かめたのか。", 1.2),
 ("series_next", "smile", "次の回は、ロボットに戻ります。AIに、ブロックをつかめたかを判定させたら、どうなったか。", 1.8),
 # 10 アウトロ
 ("outro", "smile", "この動画が役に立ったら、高評価とチャンネル登録をお願いします。"),
 ("outro", "base", "ロボットの開発や導入の相談は、概要欄の連絡先から、エイイチに直接どうぞ。"),
 ("outro", "wink", "コメントは、エイイチが全部読んでいます。それではまた。", 1.8),
]

# エイイチの声で読む文（槙原の声を流用・video_rules.md 1-4）。ハコブくんの口は動かさない。2026-11-01 確認用1回目でエイイチの指示「おれの登場とセリフ（音声）がない」で足した
EI_LINES = [i for i, seg in enumerate(SEGS) if seg[2].startswith((
    "声のイントネーションも、イラストの",
    "声と絵は何度か試して",
    "怖いと思うことは、ないな",
    "これは、どうなんだろう",
))]

# 「エイイチ」を残す文（自己紹介・目次の最後・見立ての入り・アウトロの2文のほかに、名前が要る文）。文の頭で探す（video_rules.md 3-1）
NAME_OK = [i + 1 for i, seg in enumerate(SEGS) if seg[2].startswith(("ここで、作っている本人に", "謎解きの回で、右下に"))]

READ = [
 ("Pixels to Motors", "ピクセルズ トゥー モーターズ"), ("ElevenLabs", "イレブンラボ"), ("Gemini", "ジェミニ"), ("Claude", "クロード"),
 ("フィジカルAI", "フィジカルエーアイ"), ("AI", "エーアイ"), ("1人", "ひとり"), ("1つ", "ひとつ"), ("2つ", "ふたつ"), ("3つ", "みっつ"),
 ("栄一", "えいいち"), ("「", ""), ("」", ""), ("『", ""), ("』", ""), ("｜", ""), ("…", ""),
]

ELEVEN_READ = [
    ('エイイチ', '栄一'),   # video_rules.md 3-1
    ('4コママンガ', 'よんコマまんが'),
    ('Claude', 'クロード'),
    ('ElevenLabs', 'イレブンラボ'),
    ('描く', 'えがく'), ('描き', 'えがき'), ('描か', 'えがか'), ('描いて', 'えがいて'), ('描いた', 'えがいた'), ('描け', 'えがけ'),
    ('係', 'カカリ'),
    ('物差し', 'ものさし'), ('人型', 'ひとがた'),
    # この回で先回りして足す読み（--try で確かめる）
    ('Gemini', 'ジェミニ'),
    ('第121話', 'だいひゃくにじゅういちわ'), ('第131話', 'だいひゃくさんじゅういちわ'),
    ('130話', 'ひゃくさんじゅうわ'),
    ('1話目', 'いちわめ'),
    ('3コマ目', 'さんコマめ'),
    ('207組', 'にひゃくななくみ'),
    ('14文', 'じゅうよんぶん'),
    ('何話', 'なんわ'),   # 2026-11-01 確認用で「なんはなし」と読んだ（エイイチが見つけた）
    ('22行', 'にじゅうにぎょう'),
    ('99話', 'きゅうじゅうきゅうわ'),
    ('高杉', 'たかすぎ'),
]

OVERRIDE = {}

def reading(t):
    t = OVERRIDE.get(t, t)
    for a, b in READ:
        t = t.replace(a, b)
    return t


def parse(seg):
    """1行を (場面, 表情, 字幕, 間) にそろえる。表情の欄がない古い形 (場面, 字幕[, 間]) も読む"""
    if len(seg) >= 3 and seg[1] in FACES:
        scene, face, text = seg[0], seg[1], seg[2]
        pause = seg[3] if len(seg) > 3 else 0.35
    else:
        scene, face, text = seg[0], "base", seg[1]
        pause = seg[2] if len(seg) > 2 else 0.35
    return scene, face, text, pause
