-
-
Notifications
You must be signed in to change notification settings - Fork 143
AivisSpeechに話者リスト更新ボタン追加 #228
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
Walkthroughこのプルリクエストでは、複数のローカライズファイルに新しいキー「UpdateSpeakerList」が追加され、各言語に対応する翻訳が提供されました。さらに、音声設定に関連する「Voice」コンポーネントの変更が行われ、スピーカーリストを更新するためのボタンが追加されました。また、新しいAPIハンドラーが導入され、スピーカーのデータを処理する機能が追加されました。全体として、ローカリゼーションとユーザーインターフェースの機能が強化されています。 Changes
Possibly related PRs
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this 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
📒 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関連の設定項目の近くに論理的に配置されています
- 機能の目的を明確に表現しています
| export default async function handler( | ||
| req: NextApiRequest, | ||
| res: NextApiResponse | ||
| ) { | ||
| try { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
リクエストメソッドの検証を追加してください
セキュリティ上の理由から、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.
| 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 { |
| const response = await fetch('http://127.0.0.1:10101/speakers') | ||
| const speakers: Speaker[] = await response.json() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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() |
| // Aivis形式に変換 | ||
| const aivisSpeakers: AivisSpeaker[] = speakers.flatMap((speaker) => | ||
| speaker.styles.map((style) => ({ | ||
| speaker: `${speaker.name}/${style.name}`, | ||
| id: style.id, | ||
| })) | ||
| ) |
There was a problem hiding this comment.
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.
| // 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') | |
| } |
| const filePath = path.join( | ||
| process.cwd(), | ||
| 'src/components/speakers_aivis.json' | ||
| ) | ||
| await fs.writeFile(filePath, JSON.stringify(aivisSpeakers, null, 2) + '\n') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ファイルパスの設定を改善してください
ファイルパスがハードコードされており、環境に依存する可能性があります。また、ファイルの存在チェックも必要です。
+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.
| 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') |
| } catch (error) { | ||
| console.error('Error updating speakers:', error) | ||
| res.status(500).json({ error: 'Failed to update speakers file' }) | ||
| } |
There was a problem hiding this comment.
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.
| } 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' }) | |
| } |
| speakers_aivis.splice( | ||
| 0, | ||
| speakers_aivis.length, | ||
| ...updatedSpeakers | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
配列の直接変更を避けてください
インポートされたJSONファイルの配列を直接変更することは避けるべきです。代わりにステート管理を使用してください。
以下のような実装を推奨します:
- コンポーネントの先頭で状態を管理:
const [speakersList, setSpeakersList] = useState(speakers_aivis);- JSXで
speakers_aivisの代わりにspeakersListを使用:
-{speakers_aivis.map((speaker) => (
+{speakersList.map((speaker) => (- 更新時は
setSpeakersListを使用:
-speakers_aivis.splice(0, speakers_aivis.length, ...updatedSpeakers)
+setSpeakersList(updatedSpeakers)| <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> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
エラー処理とユーザーフィードバックの追加が必要です
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.
…-for-aivis AivisSpeechに話者リスト更新ボタン追加
Summary by CodeRabbit