From c2436bfef56436fb861a5c681fdd300d89b2788a Mon Sep 17 00:00:00 2001 From: Azalea <22280294+hykilpikonna@users.noreply.github.com> Date: Wed, 19 Nov 2025 19:45:25 +0800 Subject: [PATCH] [O] Improve kana labeling --- src/lib/server/tools/lyrics.ts | 68 +++++++++++++++++----------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/lib/server/tools/lyrics.ts b/src/lib/server/tools/lyrics.ts index ef2d2c1..ee3ddff 100644 --- a/src/lib/server/tools/lyrics.ts +++ b/src/lib/server/tools/lyrics.ts @@ -1,5 +1,6 @@ import OpenAI from 'openai' import type { LyricLine, LyricSegment } from '../../../shared/types' +import { isKanji } from 'wanakana' // Please put OPENAI_API_KEY in your environment variables. const client = new OpenAI() @@ -17,17 +18,18 @@ const req = { タスク タイムスタンプ付きの日本語の歌詞テキスト(LRC形式)を入力として受け取ります。 -このテキストを解析し、指定された形式の「ふりがな付きテキスト」に変換してください。 +このテキストを解析し、指定された形式の「ふりがな・読み仮名付きテキスト」に変換してください。 処理ルール 1. **メタデータの除外**: "作词"、"作曲"、"编曲"など、歌詞以外の情報を含む行はすべて除外してください。 2. **出力形式とタイムスタンプ**: 1行につき \`[タイムスタンプ] ふりがな付き歌詞\` の形式で出力してください。 * **重要**: タイムスタンプの形式(例: \`[mm:ss.sss]\`)や数値は一切変更せず、入力されたままの状態で維持してください。 -3. **ふりがな**: 歌詞本文では、**全ての漢字**に \`漢字(ふりがな)\` の形式でふりがな(ルビ)を付けてください。 -4. **その他**: ひらがな、カタカナ、句読点、ローマ字などはそのまま出力してください。 +3. **ふりがな(漢字)**: 歌詞本文では、**全ての漢字**に \`漢字(ふりがな)\` の形式でふりがな(ルビ)を付けてください。 +4. **読み仮名(非日本語)**: 英語などの非日本語の単語には、\`Word(カタカナ読み)\` の形式でカタカナの読み仮名を付けてください。(例: \`Good(グッド)\`)。 +5. **その他**: ひらがな、カタカナ、句読点などはそのまま出力してください。 重要 -* **全ての漢字にふりがなを付けてください。** 読み方が不明な場合でも、最も一般的だと思われる読み方を付けてください。漢字をそのまま残してはいけません。 +* **全ての漢字および非日本語単語に読み仮名を付けてください。** 読み方が不明な場合でも、文脈から最も一般的だと思われる読み方を付けてください。漢字をそのまま残してはいけません。 * 以下の例に示されている形式とまったく同じ出力を生成してください。 例 @@ -35,7 +37,10 @@ const req = { [00:19.270] 目(め)に 浮(う)かぶ 九十九里浜(くじゅうくりはま) [00:22.600] 皺々(しわしわ)の 祖母(そぼ)の 手(て)を 離(はな)れ [00:25.880] 独(ひと)り で 訪(おとず)れた 歓楽街(かんらくがい) -[00:29.840] ママは 此処(ここ)の 女王様(じょおうさま)` +[00:29.840] ママは 此処(ここ)の 女王様(じょおうさま) + +例(英語単語) +[00:30.000] My(マイ) Love(ラブ) is(イズ) True(トゥルー)` }, { role: "user", @@ -55,20 +60,7 @@ const req = { [00:50.710]毎週金曜日に来ていた [00:53.970]男と暮らすのだろう [01:03.840]「一度栄えし者でも -[01:07.370]必ずや衰えゆく」 -[01:10.740]その意味を知る時を迎え -[01:13.990]足を踏み入れたは歓楽街 -[01:17.440]消えて行った女を憎めど夏は今 -[01:24.480]女王と云う肩書きを誇らしげに掲げる -[01:58.370]女に成ったあたしが -[02:01.950]売るのは自分だけで -[02:05.440]同情を欲したときに -[02:08.790]全てを失うだろう -[02:12.720]JR新宿駅の東口を出たら -[02:19.740]其処はあたしの庭 -[02:23.030]大遊戯場歌舞伎町 -[02:33.740]今夜からは此の町で -[02:39.300]娘のあたしが女王` +[01:07.370]必ずや衰えゆく」` }, { "role": "assistant", @@ -85,20 +77,7 @@ const req = { [00:50.710] 毎週(まいしゅう) 金曜日(きんようび)に 来(き)ていた [00:53.970] 男(おとこ)と 暮(く)らすのだろう [01:03.840] 「 一度(いちど) 栄(さか)えし 者(もの)でも -[01:07.370] 必(かなら)ずや 衰(おとろ)えゆく」 -[01:10.740] その 意味(いみ)を 知(し)る 時(とき)を 迎(むか)え -[01:13.990] 足(あし)を 踏(ふ)み 入(い)れたは 歓楽街(かんらくがい) -[01:17.440] 消(き)えて 行(い)った 女(おんな)を 憎(にく)めど 夏(なつ)は 今(いま) -[01:24.480] 女王(じょおう)と 云(い)う 肩書(かたが)きを 誇(ほこ)らしげに 掲(かか)げる -[01:58.370] 女(おんな)に 成(な)ったあたしが -[02:01.950] 売(う)るのは 自分(じぶん)だけで -[02:05.440] 同情(どうじょう)を 欲(ほ)したときに -[02:08.790] 全(すべ)てを 失(うしな)うだろう -[02:12.720] JR 新宿駅(しんじゅくえき)の 東口(ひがしぐち)を 出(で)たら -[02:19.740] 其処(そこ)はあたしの 庭(にわ) -[02:23.030] 大遊戯場(だいゆうぎじょう) 歌舞伎町(かぶきちょう) -[02:33.740] 今夜(こんや)からは 此(こ)の 町(まち)で -[02:39.300] 娘(むすめ)のあたしが 女王(じょおう)` +[01:07.370] 必(かなら)ずや 衰(おとろ)えゆく」` }, { role: "user", @@ -120,6 +99,10 @@ const req = { * Exmaple: * Input: `[00:15.570] 蝉(せみ)の 声(こえ)を 聞(き)く 度(たび)に` * Output: { time: "00:15.570", lyric: [ ["蝉", "せみ"], "の", ["声", "こえ"], "を", ["聞", "き"], "く", ["度", "たび"], "に" ] } + * + * Exmaple: + * Input: `[00:15.570] 「君(きみ)は綺麗(きれい)だ」` + * Output: { time: "00:15.570", lyric: [ "「", ["君", "きみ"], "は", ["綺麗", "きれい"], "だ", "」" ] } */ function parseFuriganaText(text: string): LyricLine[] { const segments: LyricLine[] = [] @@ -145,7 +128,24 @@ function parseFuriganaText(text: string): LyricLine[] { while ((match = tokenRegex.exec(lyricText)) !== null) { const [, kanji, furigana, plain] = match - if (kanji && furigana) lyric.push([kanji, furigana]) + if (kanji && furigana) { + let splitIndex = -1 + for (let i = kanji.length - 1; i >= 0; i--) { + if (!isKanji(kanji[i])) { + splitIndex = i + break + } + } + + if (splitIndex !== -1 && splitIndex < kanji.length - 1) { + const prefix = kanji.substring(0, splitIndex + 1) + const trueKanji = kanji.substring(splitIndex + 1) + lyric.push(prefix) + lyric.push([trueKanji, furigana]) + } else { + lyric.push([kanji, furigana]) + } + } else if (plain) lyric.push(plain) }