diff --git a/web/public/locales/en/components.json b/web/public/locales/en/components.json index 32feeb5..9240b4d 100644 --- a/web/public/locales/en/components.json +++ b/web/public/locales/en/components.json @@ -98,6 +98,8 @@ "pleaseSelectSpeaker": "Please select a speaker", "pleaseSelectAtLeastOneSpeaker": "Please select at least one podcast speaker.", "podcastGenerationFailed": "Podcast generation failed:", + "textTruncated": "Text Truncated", + "textTruncatedMessage": "After switching to the current mode, the text has been automatically truncated to {{maxChars}} characters to comply with the limit.", "maximum5Speakers": "You can select up to 5 speakers.", "chinese": "Chinese", "english": "English", diff --git a/web/public/locales/en/errors.json b/web/public/locales/en/errors.json index c4e9937..4f47fe2 100644 --- a/web/public/locales/en/errors.json +++ b/web/public/locales/en/errors.json @@ -26,6 +26,7 @@ "user_not_logged_in_or_session_expired": "User not logged in or session expired", "request_body_cannot_be_empty": "Request body cannot be empty", "tts_provider_cannot_be_empty": "TTS provider cannot be empty", + "input_text_exceeds_limit": "Input text exceeds limit, maximum {{limit}} characters allowed", "please_select_at_least_one_speaker": "Please select at least one podcast speaker", "invalid_speaker_config_format": "Invalid podcast speaker configuration format", "insufficient_points_for_podcast": "Insufficient points, generating a podcast requires {{pointsNeeded}} points, you currently have {{currentPoints}} points.", diff --git a/web/public/locales/ja/components.json b/web/public/locales/ja/components.json index 3d658c3..42df231 100644 --- a/web/public/locales/ja/components.json +++ b/web/public/locales/ja/components.json @@ -98,6 +98,8 @@ "pleaseSelectSpeaker": "スピーカーを選択してください", "pleaseSelectAtLeastOneSpeaker": "少なくとも1人のポッドキャストスピーカーを選択してください。", "podcastGenerationFailed": "ポッドキャストの生成に失敗しました:", + "textTruncated": "テキストが切り詰められました", + "textTruncatedMessage": "現在のモードに切り替えた後、テキストは制限に準拠するために自動的に {{maxChars}} 文字に切り詰められました。", "maximum5Speakers": "最大5人のスピーカーを選択できます。", "chinese": "中国語", "english": "英語", diff --git a/web/public/locales/ja/errors.json b/web/public/locales/ja/errors.json index fbd7bb6..8915b3e 100644 --- a/web/public/locales/ja/errors.json +++ b/web/public/locales/ja/errors.json @@ -26,6 +26,7 @@ "user_not_logged_in_or_session_expired": "ユーザーがログインしていないか、セッションの有効期限が切れています", "request_body_cannot_be_empty": "リクエストボディは空にできません", "tts_provider_cannot_be_empty": "TTSプロバイダーは空にできません", + "input_text_exceeds_limit": "入力テキストが制限を超えています。最大 {{limit}} 文字まで許可されています", "please_select_at_least_one_speaker": "少なくとも1人のポッドキャスト話者を選択してください", "invalid_speaker_config_format": "無効なポッドキャスト話者設定フォーマット", "insufficient_points_for_podcast": "ポイントが不足しています。ポッドキャストを生成するには{{pointsNeeded}}ポイントが必要です。現在{{currentPoints}}ポイントしかありません。", diff --git a/web/public/locales/zh-CN/components.json b/web/public/locales/zh-CN/components.json index cb952f5..e196738 100644 --- a/web/public/locales/zh-CN/components.json +++ b/web/public/locales/zh-CN/components.json @@ -98,6 +98,8 @@ "pleaseSelectSpeaker": "请选择说话人", "pleaseSelectAtLeastOneSpeaker": "请至少选择一位播客说话人。", "podcastGenerationFailed": "播客生成失败:", + "textTruncated": "文本已截断", + "textTruncatedMessage": "切换到当前模式后,文本已自动截断至 {{maxChars}} 字符以符合限制。", "maximum5Speakers": "最多只能选择5个说话人。", "chinese": "中文", "english": "英文", diff --git a/web/public/locales/zh-CN/errors.json b/web/public/locales/zh-CN/errors.json index 2f59e8c..0171126 100644 --- a/web/public/locales/zh-CN/errors.json +++ b/web/public/locales/zh-CN/errors.json @@ -26,6 +26,7 @@ "user_not_logged_in_or_session_expired": "用户未登录或会话已过期", "request_body_cannot_be_empty": "请求正文不能为空", "tts_provider_cannot_be_empty": "TTS服务提供商不能为空", + "input_text_exceeds_limit": "输入文本超过限制,最多允许 {{limit}} 字符", "please_select_at_least_one_speaker": "请至少选择一位播客说话人", "invalid_speaker_config_format": "播客说话人配置格式无效", "insufficient_points_for_podcast": "积分不足,生成一个播客需要 {{pointsNeeded}} 积分,您当前只有 {{currentPoints}} 积分。", diff --git a/web/src/app/api/generate-podcast-with-story/route.ts b/web/src/app/api/generate-podcast-with-story/route.ts index 8a3f900..1d09a13 100644 --- a/web/src/app/api/generate-podcast-with-story/route.ts +++ b/web/src/app/api/generate-podcast-with-story/route.ts @@ -33,6 +33,15 @@ export async function POST(request: NextRequest) { { status: 400 } ); } + + // 字符数限制校验 - 沉浸故事模式限制30000字符 + const MAX_CHARS_AI_STORY = 30000; + if (body.input_txt_content.length > MAX_CHARS_AI_STORY) { + return NextResponse.json( + { success: false, error: t('input_text_exceeds_limit', { limit: MAX_CHARS_AI_STORY }) }, + { status: 400 } + ); + } if (!body.tts_provider || body.tts_provider.trim().length === 0) { return NextResponse.json( { success: false, error: t('tts_provider_cannot_be_empty') }, diff --git a/web/src/app/api/generate-podcast/route.ts b/web/src/app/api/generate-podcast/route.ts index a2af77a..83e3e85 100644 --- a/web/src/app/api/generate-podcast/route.ts +++ b/web/src/app/api/generate-podcast/route.ts @@ -33,6 +33,15 @@ export async function POST(request: NextRequest) { { status: 400 } ); } + + // 字符数限制校验 - AI播客模式限制20000字符 + const MAX_CHARS_AI_PODCAST = 20000; + if (body.input_txt_content.length > MAX_CHARS_AI_PODCAST) { + return NextResponse.json( + { success: false, error: t('input_text_exceeds_limit', { limit: MAX_CHARS_AI_PODCAST }) }, + { status: 400 } + ); + } if (!body.tts_provider || body.tts_provider.trim().length === 0) { return NextResponse.json( { success: false, error: t('tts_provider_cannot_be_empty') }, diff --git a/web/src/components/PodcastCreator.tsx b/web/src/components/PodcastCreator.tsx index d134654..b94094d 100644 --- a/web/src/components/PodcastCreator.tsx +++ b/web/src/components/PodcastCreator.tsx @@ -68,6 +68,26 @@ const PodcastCreator: React.FC = ({ const [topic, setTopic] = useState(''); const [customInstructions, setCustomInstructions] = useState(''); const [selectedMode, setSelectedMode] = useState<'ai-podcast' | 'ai-story'>('ai-podcast'); + + // 字符数限制常量 + const MAX_CHARS_AI_PODCAST = 20000; + const MAX_CHARS_AI_STORY = 30000; + + // 获取当前模式的字符数限制 + const maxChars = selectedMode === 'ai-podcast' ? MAX_CHARS_AI_PODCAST : MAX_CHARS_AI_STORY; + + // 监听模式切换,如果文本超过新模式的限制,则截断 + useEffect(() => { + if (topic.length > maxChars) { + const truncatedTopic = topic.substring(0, maxChars); + setTopic(truncatedTopic); + setItem('podcast-topic', truncatedTopic); + error( + t('podcastCreator.textTruncated'), + t('podcastCreator.textTruncatedMessage', { maxChars }) + ); + } + }, [selectedMode, maxChars]); // 只在模式切换时触发 // 初始化时从 localStorage 加载 topic 和 customInstructions useEffect(() => { @@ -366,14 +386,27 @@ const PodcastCreator: React.FC = ({