Skip to content

Conversation

@tegnike
Copy link
Owner

@tegnike tegnike commented Nov 21, 2024

Summary by CodeRabbit

  • 新機能
    • スピーカーリストを更新する新しいボタンを追加しました。
    • スピーカー選択ドロップダウンのラベルを変更しました。
  • 翻訳の改善
    • 英語、日本語、韓国語、中国語の翻訳ファイルに「UpdateSpeakerList」のエントリを追加しました。
    • 日本語の「AivisSpeechSpeaker」のラベルを「話者」に変更しました。

@vercel
Copy link

vercel bot commented Nov 21, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
aituber-kit ✅ Ready (Inspect) Visit Preview 💬 Add feedback Nov 21, 2024 3:05am

@coderabbitai
Copy link

coderabbitai bot commented Nov 21, 2024

Walkthrough

このプルリクエストでは、複数のローカライズファイルに新しいキー「UpdateSpeakerList」が追加され、各言語に対応する翻訳が提供されました。さらに、音声設定に関連する「Voice」コンポーネントの変更が行われ、スピーカーリストを更新するためのボタンが追加されました。また、新しいAPIハンドラーが導入され、スピーカーのデータを処理する機能が追加されました。全体として、ローカリゼーションとユーザーインターフェースの機能が強化されています。

Changes

ファイル 変更の概要
locales/en/translation.json 新しいエントリ「UpdateSpeakerList」を追加: "Update Speaker List"
locales/ja/translation.json キー「AivisSpeechSpeaker」を「話者」に変更し、新しいキー「UpdateSpeakerList」を追加: "話者リストを更新"
locales/ko/translation.json 新しいエントリ「UpdateSpeakerList」を追加: "보이스 타입 업데이트"
locales/zh/translation.json 新しいエントリ「UpdateSpeakerList」を追加: "更新語音角色"
src/components/settings/voice.tsx スピーカー選択ドロップダウンのラベルを変更し、「UpdateSpeakerList」ボタンを追加。APIを呼び出してスピーカーリストを更新
src/components/speakers_aivis.json スピーカーのエントリを可読性を向上させるためにフォーマットを変更
src/pages/api/update-aivis-speakers.ts 新しいAPIハンドラーを追加し、スピーカーデータを処理する機能を実装

Possibly related PRs

  • Style-Bert-VITS2の設定更新 #158: 英語の翻訳ファイルに「UpdateSpeakerList」の新しいエントリを追加する主要なPR。
  • Realtime API対応 #167: 「UpdateSpeakerList」キーに関する変更が「Voice」コンポーネントの修正に関連している。
  • タイポ修正 #180: 「UpdateSpeakerList」キーのローカライズに関する更新が、英語の翻訳ファイルの変更に関連している。
  • リリースブランチ #182: 「UpdateSpeakerList」キーの追加が、さまざまな設定や機能の修正に関連している。
  • ドキュメント更新 #204: 「UpdateSpeakerList」キーの追加が、さまざまな設定や指示の明確化に関連しているドキュメントの更新に関連している。
  • 本番リリース #210: 「UpdateSpeakerList」キーに関する変更が、プロジェクトの使用ポリシーや設定に関するドキュメントの更新に関連している。

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Outside diff range and nitpick comments (1)
src/pages/api/update-aivis-speakers.ts (1)

5-20: インターフェースにドキュメンテーションを追加することを推奨します

各インターフェースの目的と使用方法を明確にするため、JSDocコメントの追加を提案します。

+/**
+ * VOICEVOXのスタイル情報を表すインターフェース
+ */
 interface Style {
   name: string
   id: number
   type: string
 }

+/**
+ * VOICEVOXの話者情報を表すインターフェース
+ */
 interface Speaker {
   name: string
   speaker_uuid: string
   styles: Style[]
 }

+/**
+ * Aivis用に変換された話者情報を表すインターフェース
+ */
 interface AivisSpeaker {
   speaker: string
   id: number
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 8a63d7b and d0e14e2.

📒 Files selected for processing (7)
  • locales/en/translation.json (1 hunks)
  • locales/ja/translation.json (1 hunks)
  • locales/ko/translation.json (1 hunks)
  • locales/zh/translation.json (1 hunks)
  • src/components/settings/voice.tsx (2 hunks)
  • src/components/speakers_aivis.json (1 hunks)
  • src/pages/api/update-aivis-speakers.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/components/speakers_aivis.json
🔇 Additional comments (5)
locales/zh/translation.json (1)

66-66: 翻訳の追加を承認します

翻訳の追加位置とその内容が適切です。他の AivisSpeech 関連の設定項目と同じセクションに配置されており、文脈的にも問題ありません。

他の言語ファイルとの一貫性を確認するため、以下のスクリプトを実行します:

✅ Verification successful

翻訳の一貫性が確認できました

各言語ファイルで "UpdateSpeakerList" キーが適切に実装されており、それぞれの翻訳も意味的に整合性が取れています:

  • 英語:Update Speaker List
  • 日本語:話者リストを更新
  • 韓国語:보이스 타입 업데이트
  • 中国語:更新語音角色

全ての翻訳が同じ概念を適切に表現しており、各言語の自然な表現方法で実装されています。

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# 説明:他の言語ファイルで "UpdateSpeakerList" キーが同様に実装されているか確認します

# 全ての言語ファイルから "UpdateSpeakerList" キーを検索
for locale in en ja ko; do
  echo "Checking ${locale}/translation.json:"
  rg -A 1 '"UpdateSpeakerList"' "locales/${locale}/translation.json"
done

Length of output: 690

locales/ja/translation.json (2)

62-62: 用語の変更が適切です

「ボイスタイプ」から「話者」への変更は、より正確で一般的な用語となっており、機能の目的をより明確に表現しています。


66-66: UIテキストの追加が適切です

「話者リストを更新」という文言は、機能の目的を簡潔かつ明確に表現しており、ユーザーにとって分かりやすい表現となっています。

locales/ko/translation.json (1)

66-66: 翻訳の追加が適切に行われています

「UpdateSpeakerList」の韓国語訳が他のUIエレメントと一貫性のある形で実装されており、AivisSpeechの機能拡張に適切に対応しています。

locales/en/translation.json (1)

66-66: 新しい翻訳エントリが適切に追加されています!

翻訳キー「UpdateSpeakerList」は以下の点で適切に実装されています:

  • 他のボタンラベルと同じ命名規則に従っています
  • AivisSpeech関連の設定項目の近くに論理的に配置されています
  • 機能の目的を明確に表現しています

Comment on lines +22 to +26
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
try {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

リクエストメソッドの検証を追加してください

セキュリティ上の理由から、POSTメソッドのみを許可するように制限することを推奨します。

 export default async function handler(
   req: NextApiRequest,
   res: NextApiResponse
 ) {
+  if (req.method !== 'POST') {
+    return res.status(405).json({ error: 'Method not allowed' })
+  }
   try {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
try {
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' })
}
try {

Comment on lines +28 to +29
const response = await fetch('http://127.0.0.1:10101/speakers')
const speakers: Speaker[] = await response.json()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

URLを環境変数として設定することを推奨します

ハードコードされたURLは環境に応じて変更が必要となるため、環境変数として設定すべきです。

+const VOICEVOX_API_URL = process.env.VOICEVOX_API_URL || 'http://127.0.0.1:10101'
-    const response = await fetch('http://127.0.0.1:10101/speakers')
+    const response = await fetch(`${VOICEVOX_API_URL}/speakers`)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const response = await fetch('http://127.0.0.1:10101/speakers')
const speakers: Speaker[] = await response.json()
const VOICEVOX_API_URL = process.env.VOICEVOX_API_URL || 'http://127.0.0.1:10101'
const response = await fetch(`${VOICEVOX_API_URL}/speakers`)
const speakers: Speaker[] = await response.json()

Comment on lines +31 to +37
// Aivis形式に変換
const aivisSpeakers: AivisSpeaker[] = speakers.flatMap((speaker) =>
speaker.styles.map((style) => ({
speaker: `${speaker.name}/${style.name}`,
id: style.id,
}))
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

データ変換処理の堅牢性を向上させてください

スピーカーデータの変換処理において、無効なデータの検証が行われていません。

     // Aivis形式に変換
+    if (!Array.isArray(speakers)) {
+      throw new TypeError('Speakers data must be an array')
+    }
     const aivisSpeakers: AivisSpeaker[] = speakers.flatMap((speaker) =>
-      speaker.styles.map((style) => ({
+      speaker.styles?.map((style) => ({
         speaker: `${speaker.name}/${style.name}`,
         id: style.id,
-      }))
+      })) ?? []
     )
+    
+    if (aivisSpeakers.length === 0) {
+      throw new Error('No valid speakers found')
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Aivis形式に変換
const aivisSpeakers: AivisSpeaker[] = speakers.flatMap((speaker) =>
speaker.styles.map((style) => ({
speaker: `${speaker.name}/${style.name}`,
id: style.id,
}))
)
// Aivis形式に変換
if (!Array.isArray(speakers)) {
throw new TypeError('Speakers data must be an array')
}
const aivisSpeakers: AivisSpeaker[] = speakers.flatMap((speaker) =>
speaker.styles?.map((style) => ({
speaker: `${speaker.name}/${style.name}`,
id: style.id,
})) ?? []
)
if (aivisSpeakers.length === 0) {
throw new Error('No valid speakers found')
}

Comment on lines +40 to +44
const filePath = path.join(
process.cwd(),
'src/components/speakers_aivis.json'
)
await fs.writeFile(filePath, JSON.stringify(aivisSpeakers, null, 2) + '\n')
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

ファイルパスの設定を改善してください

ファイルパスがハードコードされており、環境に依存する可能性があります。また、ファイルの存在チェックも必要です。

+const SPEAKERS_FILE_PATH = process.env.SPEAKERS_FILE_PATH || 'src/components/speakers_aivis.json'
-    const filePath = path.join(
-      process.cwd(),
-      'src/components/speakers_aivis.json'
-    )
+    const filePath = path.join(process.cwd(), SPEAKERS_FILE_PATH)
+    
+    // ディレクトリの存在確認
+    const directory = path.dirname(filePath)
+    try {
+      await fs.access(directory)
+    } catch {
+      await fs.mkdir(directory, { recursive: true })
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const filePath = path.join(
process.cwd(),
'src/components/speakers_aivis.json'
)
await fs.writeFile(filePath, JSON.stringify(aivisSpeakers, null, 2) + '\n')
const SPEAKERS_FILE_PATH = process.env.SPEAKERS_FILE_PATH || 'src/components/speakers_aivis.json'
const filePath = path.join(process.cwd(), SPEAKERS_FILE_PATH)
// ディレクトリの存在確認
const directory = path.dirname(filePath)
try {
await fs.access(directory)
} catch {
await fs.mkdir(directory, { recursive: true })
}
await fs.writeFile(filePath, JSON.stringify(aivisSpeakers, null, 2) + '\n')

Comment on lines +47 to +50
} catch (error) {
console.error('Error updating speakers:', error)
res.status(500).json({ error: 'Failed to update speakers file' })
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

エラーハンドリングを改善してください

現在のエラーハンドリングは基本的すぎます。より詳細なエラー情報の提供と、エラータイプの区別が必要です。

-  } catch (error) {
-    console.error('Error updating speakers:', error)
-    res.status(500).json({ error: 'Failed to update speakers file' })
+  } catch (error: unknown) {
+    console.error('Error updating speakers:', error)
+    if (error instanceof TypeError) {
+      return res.status(400).json({ error: 'Invalid API response format' })
+    }
+    if (error instanceof Error) {
+      return res.status(500).json({ error: error.message })
+    }
+    return res.status(500).json({ error: 'An unexpected error occurred' })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch (error) {
console.error('Error updating speakers:', error)
res.status(500).json({ error: 'Failed to update speakers file' })
}
} catch (error: unknown) {
console.error('Error updating speakers:', error)
if (error instanceof TypeError) {
return res.status(400).json({ error: 'Invalid API response format' })
}
if (error instanceof Error) {
return res.status(500).json({ error: error.message })
}
return res.status(500).json({ error: 'An unexpected error occurred' })
}

Comment on lines +477 to +481
speakers_aivis.splice(
0,
speakers_aivis.length,
...updatedSpeakers
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

配列の直接変更を避けてください

インポートされたJSONファイルの配列を直接変更することは避けるべきです。代わりにステート管理を使用してください。

以下のような実装を推奨します:

  1. コンポーネントの先頭で状態を管理:
const [speakersList, setSpeakersList] = useState(speakers_aivis);
  1. JSXでspeakers_aivisの代わりにspeakersListを使用:
-{speakers_aivis.map((speaker) => (
+{speakersList.map((speaker) => (
  1. 更新時はsetSpeakersListを使用:
-speakers_aivis.splice(0, speakers_aivis.length, ...updatedSpeakers)
+setSpeakersList(updatedSpeakers)

Comment on lines +466 to +487
<TextButton
onClick={async () => {
const response = await fetch('/api/update-aivis-speakers')
if (response.ok) {
// 話者リストを再読み込み
const updatedSpeakersResponse = await fetch(
'/speakers_aivis.json'
)
const updatedSpeakers =
await updatedSpeakersResponse.json()
// speakers_aivisを更新
speakers_aivis.splice(
0,
speakers_aivis.length,
...updatedSpeakers
)
}
}}
className="ml-16"
>
{t('UpdateSpeakerList')}
</TextButton>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

エラー処理とユーザーフィードバックの追加が必要です

APIコールのエラー処理とユーザーフィードバックが不足しています:

  • ネットワークエラーの処理
  • APIレスポンスのバリデーション
  • 更新中のローディング状態の表示
  • 成功/失敗時のユーザーへの通知

以下のような実装を推奨します:

 <TextButton
+  disabled={isUpdating}
   onClick={async () => {
+    try {
+      setIsUpdating(true);
       const response = await fetch('/api/update-aivis-speakers')
       if (response.ok) {
         // 話者リストを再読み込み
         const updatedSpeakersResponse = await fetch(
           '/speakers_aivis.json'
         )
+        if (!updatedSpeakersResponse.ok) {
+          throw new Error('話者リストの取得に失敗しました');
+        }
         const updatedSpeakers =
           await updatedSpeakersResponse.json()
         // speakers_aivisを更新
-        speakers_aivis.splice(
-          0,
-          speakers_aivis.length,
-          ...updatedSpeakers
-        )
+        setSpeakers(updatedSpeakers);
+        toast.success('話者リストを更新しました');
+      } else {
+        throw new Error('更新に失敗しました');
+      }
+    } catch (error) {
+      console.error(error);
+      toast.error(error.message);
+    } finally {
+      setIsUpdating(false);
+    }
   }}
   className="ml-16"
 >
-  {t('UpdateSpeakerList')}
+  {isUpdating ? t('Updating...') : t('UpdateSpeakerList')}
 </TextButton>

Committable suggestion skipped: line range outside the PR's diff.

@tegnike tegnike merged commit 6e787af into develop Nov 21, 2024
3 checks passed
@tegnike tegnike deleted the feature/add-update-button-for-aivis branch November 21, 2024 03:16
terisuke pushed a commit to terisuke/aituber-kit that referenced this pull request Dec 5, 2025
…-for-aivis

AivisSpeechに話者リスト更新ボタン追加
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants