diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..b9b6ea8 --- /dev/null +++ b/.env.example @@ -0,0 +1,5 @@ +# Server-only credentials. Configure these in `.env.local` for local +# development and in the deployment provider's encrypted environment settings +# for production. Never commit real values. +DEEPSEEK_API_KEY=replace-with-your-siliconflow-api-key +ARK_API_KEY=replace-with-your-volcengine-ark-api-key diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..6e15051 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,40 @@ +name: CI + +on: + push: + branches: + - main + - "security/**" + - "portfolio/**" + pull_request: + +permissions: + contents: read + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5 + + - name: Set up Node.js + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5 + with: + node-version: "22" + package-manager-cache: false + + - name: Install locked dependencies + run: corepack pnpm@8.15.6 install --frozen-lockfile + + - name: Audit production dependencies + run: corepack pnpm@8.15.6 audit --prod + + - name: Type-check + run: corepack pnpm@8.15.6 exec tsc --noEmit + + - name: Build production application + env: + ARK_API_KEY: ci-placeholder + DEEPSEEK_API_KEY: ci-placeholder + run: corepack pnpm@8.15.6 build diff --git a/.gitignore b/.gitignore index f704477..1c17f94 100755 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ yarn-error.log* # local env files .env .env*.local +.history/ # vercel .vercel @@ -35,4 +36,4 @@ yarn-error.log* *.tsbuildinfo next-env.d.ts -.history \ No newline at end of file +.history diff --git a/.history/.gitignore_20250727100327 b/.history/.gitignore_20250727100327 deleted file mode 100644 index f650315..0000000 --- a/.history/.gitignore_20250727100327 +++ /dev/null @@ -1,27 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules - -# next.js -/.next/ -/out/ - -# production -/build - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* -.pnpm-debug.log* - -# env files -.env* - -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts \ No newline at end of file diff --git a/.history/.gitignore_20250803171954 b/.history/.gitignore_20250803171954 deleted file mode 100644 index c9cbdde..0000000 --- a/.history/.gitignore_20250803171954 +++ /dev/null @@ -1,36 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# local env files -.env -.env*.local - -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts \ No newline at end of file diff --git a/.history/README_20250803172033.md b/.history/README_20250803172033.md deleted file mode 100644 index 0519ecb..0000000 --- a/.history/README_20250803172033.md +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/.history/README_20250803172037.md b/.history/README_20250803172037.md deleted file mode 100644 index c4d5e1b..0000000 --- a/.history/README_20250803172037.md +++ /dev/null @@ -1,85 +0,0 @@ -# 漫画生成器 - -AI 驱动的文本到漫画转换应用,基于 Next.js 构建。 - -## 功能特点 - -- 将文本内容转换为漫画剧本 -- 根据剧本生成漫画图像 -- 多种文本生成模型选择 -- 可定制的图片比例和生成参数 -- 响应式界面设计 - -## 技术栈 - -- Next.js (React 框架) -- TypeScript -- Tailwind CSS -- DeepSeek API (文本生成) -- Volces ARK API (图像生成) - -## 本地开发 - -1. 克隆仓库 - -```bash -git clone -cd comic-generator -``` - -2. 安装依赖 - -```bash -pnpm install -``` - -3. 创建环境变量文件 - -```bash -cp .env.example .env.local -``` - -然后编辑`.env.local`文件,添加你的 API 密钥 - -4. 启动开发服务器 - -```bash -pnpm dev -``` - -5. 在浏览器中访问 [http://localhost:3000](http://localhost:3000) - -## Vercel 部署 - -### 自动部署设置 - -本项目已配置为通过 Vercel 自动部署。每次推送代码到主分支时,Vercel 都会自动构建并部署新版本。 - -设置步骤: - -1. Fork 或克隆此仓库到你的 GitHub 账号 - -2. 在[Vercel](https://vercel.com)创建账号并连接你的 GitHub 账号 - -3. 导入项目 - - - 点击"New Project" - - 选择你的仓库 - - 在"Environment Variables"部分添加必要的环境变量(如`DEEPSEEK_API_KEY`) - - 点击"Deploy"开始部署 - -4. 完成后,Vercel 会提供一个部署 URL,你可以在项目设置中自定义域名 - -### 注意事项 - -- 确保你的 API 密钥在 Vercel 项目设置中正确配置 -- 更改环境变量后需要重新部署应用 -- 使用 Vercel CLI 可以进行本地测试 - -## 贡献指南 - -欢迎贡献代码、提出问题或建议。请通过 GitHub Issues 提交问题或通过 Pull Request 贡献代码。 - -## 许可证 - -[MIT](LICENSE) diff --git a/.history/app/api/generate-images/route_20250801235053.ts b/.history/app/api/generate-images/route_20250801235053.ts deleted file mode 100644 index 0519ecb..0000000 --- a/.history/app/api/generate-images/route_20250801235053.ts +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/.history/app/api/generate-images/route_20250801235058.ts b/.history/app/api/generate-images/route_20250801235058.ts deleted file mode 100644 index bcde069..0000000 --- a/.history/app/api/generate-images/route_20250801235058.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; -import { ComicPanel } from "@/lib/types"; - -const ARK_API_URL = - "https://ark.cn-beijing.volces.com/api/v3/images/generations"; -const ARK_API_KEY = "7ef519e4-d564-4f90-8459-94b6ef973e25"; // 在实际生产环境中应使用环境变量 -const BASE_IMAGE_URL = - "https://ark-project.tos-cn-beijing.volces.com/doc_image/seededit_i2i.jpeg"; // 示例基础图像URL - -export async function POST(request: NextRequest) { - try { - const { comicPanels } = await request.json(); - - if ( - !comicPanels || - !Array.isArray(comicPanels) || - comicPanels.length === 0 - ) { - return NextResponse.json( - { error: "缺少有效的漫画剧本数据" }, - { status: 400 } - ); - } - - // 处理每个场景,生成对应的图像 - const imageGenerationPromises = comicPanels.map( - async (panel: ComicPanel, index: number) => { - try { - // 从剧本内容中提取关键信息,构建适合图像生成的提示词 - // 这里可以根据需要优化提示词的构建方式 - const prompt = `漫画风格的场景:${panel.content.substring(0, 200)}`; - - // 调用ARK图像生成API - const response = await fetch(ARK_API_URL, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${ARK_API_KEY}`, - }, - body: JSON.stringify({ - model: "doubao-seededit-3-0-i2i-250628", - prompt: prompt, - image: BASE_IMAGE_URL, - response_format: "url", - size: "adaptive", - seed: Math.floor(Math.random() * 1000), // 随机种子以获得不同的结果 - guidance_scale: 5.5, - watermark: true, - }), - }); - - if (!response.ok) { - const errorData = await response.text(); - console.error(`ARK API 错误 (场景 ${index + 1}):`, errorData); - throw new Error(`生成图像失败: ${response.statusText}`); - } - - const data = await response.json(); - return { - id: panel.id, - content: panel.content, - imageUrl: data.data?.[0]?.url || panel.imageUrl, // 如果有新图像URL则更新,否则保留原样 - sceneNumber: index + 1, - }; - } catch (err) { - console.error(`处理场景 ${index + 1} 时出错:`, err); - // 出错时返回原始面板数据,但添加错误信息 - return { - ...panel, - imageUrl: panel.imageUrl, - sceneNumber: index + 1, - error: err instanceof Error ? err.message : "图像生成失败", - }; - } - } - ); - - // 等待所有图像生成完成 - const results = await Promise.allSettled(imageGenerationPromises); - - // 处理结果 - const generatedPanels = results.map((result, index) => { - if (result.status === "fulfilled") { - return result.value; - } else { - // 如果某个请求失败,返回原始面板但带有错误信息 - return { - ...comicPanels[index], - sceneNumber: index + 1, - error: "图像生成请求失败", - }; - } - }); - - return NextResponse.json({ comicPanels: generatedPanels }); - } catch (error) { - console.error("图像生成API路由错误:", error); - return NextResponse.json({ error: "服务器内部错误" }, { status: 500 }); - } -} diff --git a/.history/app/api/generate-images/route_20250801235544.ts b/.history/app/api/generate-images/route_20250801235544.ts deleted file mode 100644 index 1b7be4e..0000000 --- a/.history/app/api/generate-images/route_20250801235544.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; -import { ComicPanel } from "@/lib/types"; - -const ARK_API_URL = - "https://ark.cn-beijing.volces.com/api/v3/images/generations"; -const ARK_API_KEY = "7ef519e4-d564-4f90-8459-94b6ef973e25"; // 在实际生产环境中应使用环境变量 -const BASE_IMAGE_URL = - "https://ark-project.tos-cn-beijing.volces.com/doc_image/seededit_i2i.jpeg"; // 示例基础图像URL - -export async function POST(request: NextRequest) { - try { - const { comicPanels, aspectRatio = "16:9" } = await request.json(); - - if ( - !comicPanels || - !Array.isArray(comicPanels) || - comicPanels.length === 0 - ) { - return NextResponse.json( - { error: "缺少有效的漫画剧本数据" }, - { status: 400 } - ); - } - - // 处理每个场景,生成对应的图像 - const imageGenerationPromises = comicPanels.map( - async (panel: ComicPanel, index: number) => { - try { - // 从剧本内容中提取关键信息,构建适合图像生成的提示词 - // 这里可以根据需要优化提示词的构建方式 - const prompt = `漫画风格的场景:${panel.content.substring(0, 200)}`; - - // 根据用户选择的比例确定输出尺寸 - let size = "adaptive"; // 默认自适应尺寸 - - if (aspectRatio !== "custom") { - // 如果用户选择了预设的比例,则使用对应的尺寸 - // 由于API可能没有直接支持比例参数,我们在这里注释了可能的处理方式 - // 实际使用时可以根据API的文档进行适配 - - // 以下仅为示意,实际API可能使用不同的参数 - size = aspectRatio; - } - - // 调用ARK图像生成API - const response = await fetch(ARK_API_URL, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${ARK_API_KEY}`, - }, - body: JSON.stringify({ - model: "doubao-seededit-3-0-i2i-250628", - prompt: prompt, - image: BASE_IMAGE_URL, - response_format: "url", - size: size, // 使用用户选择的比例 - aspect_ratio: aspectRatio, // 添加额外的比例参数(如果API支持) - seed: Math.floor(Math.random() * 1000), // 随机种子以获得不同的结果 - guidance_scale: 5.5, - watermark: true, - }), - }); - - if (!response.ok) { - const errorData = await response.text(); - console.error(`ARK API 错误 (场景 ${index + 1}):`, errorData); - throw new Error(`生成图像失败: ${response.statusText}`); - } - - const data = await response.json(); - return { - id: panel.id, - content: panel.content, - imageUrl: data.data?.[0]?.url || panel.imageUrl, // 如果有新图像URL则更新,否则保留原样 - sceneNumber: index + 1, - }; - } catch (err) { - console.error(`处理场景 ${index + 1} 时出错:`, err); - // 出错时返回原始面板数据,但添加错误信息 - return { - ...panel, - imageUrl: panel.imageUrl, - sceneNumber: index + 1, - error: err instanceof Error ? err.message : "图像生成失败", - }; - } - } - ); - - // 等待所有图像生成完成 - const results = await Promise.allSettled(imageGenerationPromises); - - // 处理结果 - const generatedPanels = results.map((result, index) => { - if (result.status === "fulfilled") { - return result.value; - } else { - // 如果某个请求失败,返回原始面板但带有错误信息 - return { - ...comicPanels[index], - sceneNumber: index + 1, - error: "图像生成请求失败", - }; - } - }); - - return NextResponse.json({ comicPanels: generatedPanels }); - } catch (error) { - console.error("图像生成API路由错误:", error); - return NextResponse.json({ error: "服务器内部错误" }, { status: 500 }); - } -} diff --git a/.history/app/api/generate-images/route_20250801235831.ts b/.history/app/api/generate-images/route_20250801235831.ts deleted file mode 100644 index db37c7a..0000000 --- a/.history/app/api/generate-images/route_20250801235831.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; -import { ComicPanel } from "@/lib/types"; - -const ARK_API_URL = - "https://ark.cn-beijing.volces.com/api/v3/images/generations"; -const ARK_API_KEY = "7ef519e4-d564-4f90-8459-94b6ef973e25"; // 在实际生产环境中应使用环境变量 -const BASE_IMAGE_URL = - "https://ark-project.tos-cn-beijing.volces.com/doc_image/seededit_i2i.jpeg"; // 示例基础图像URL - -// 将比例转换为图像尺寸的映射 -const aspectRatioToSize = { - "1:1": "1024x1024", - "3:4": "768x1024", - "4:3": "1024x768", - "16:9": "1024x576", - "9:16": "576x1024", - "2:3": "768x1152", - "3:2": "1152x768", - "21:9": "1344x576", - custom: "1024x1024", // 默认为1:1 -}; - -export async function POST(request: NextRequest) { - try { - const { comicPanels, aspectRatio = "16:9" } = await request.json(); - - if ( - !comicPanels || - !Array.isArray(comicPanels) || - comicPanels.length === 0 - ) { - return NextResponse.json( - { error: "缺少有效的漫画剧本数据" }, - { status: 400 } - ); - } - - // 处理每个场景,生成对应的图像 - const imageGenerationPromises = comicPanels.map( - async (panel: ComicPanel, index: number) => { - try { - // 从剧本内容中提取关键信息,构建适合图像生成的提示词 - const prompt = `漫画风格的场景:${panel.content.substring(0, 200)}`; - - // 获取对应比例的尺寸 - const size = aspectRatioToSize[aspectRatio] || "1024x1024"; - - // 调用ARK图像生成API - const response = await fetch(ARK_API_URL, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${ARK_API_KEY}`, - }, - body: JSON.stringify({ - model: "doubao-seededit-3-0-i2i-250628", - prompt: prompt, - image: BASE_IMAGE_URL, - response_format: "url", - size: size, // 使用转换后的尺寸值 - seed: Math.floor(Math.random() * 1000), // 随机种子以获得不同的结果 - guidance_scale: 5.5, - watermark: true, - }), - }); - - if (!response.ok) { - const errorData = await response.text(); - console.error(`ARK API 错误 (场景 ${index + 1}):`, errorData); - throw new Error(`生成图像失败: ${response.statusText}`); - } - - const data = await response.json(); - return { - id: panel.id, - content: panel.content, - imageUrl: data.data?.[0]?.url || panel.imageUrl, // 如果有新图像URL则更新,否则保留原样 - sceneNumber: index + 1, - }; - } catch (err) { - console.error(`处理场景 ${index + 1} 时出错:`, err); - // 出错时返回原始面板数据,但添加错误信息 - return { - ...panel, - imageUrl: panel.imageUrl, - sceneNumber: index + 1, - error: err instanceof Error ? err.message : "图像生成失败", - }; - } - } - ); - - // 等待所有图像生成完成 - const results = await Promise.allSettled(imageGenerationPromises); - - // 处理结果 - const generatedPanels = results.map((result, index) => { - if (result.status === "fulfilled") { - return result.value; - } else { - // 如果某个请求失败,返回原始面板但带有错误信息 - return { - ...comicPanels[index], - sceneNumber: index + 1, - error: "图像生成请求失败", - }; - } - }); - - return NextResponse.json({ comicPanels: generatedPanels }); - } catch (error) { - console.error("图像生成API路由错误:", error); - return NextResponse.json({ error: "服务器内部错误" }, { status: 500 }); - } -} diff --git a/.history/app/api/generate-images/route_20250801235908.ts b/.history/app/api/generate-images/route_20250801235908.ts deleted file mode 100644 index ae5d267..0000000 --- a/.history/app/api/generate-images/route_20250801235908.ts +++ /dev/null @@ -1,128 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; -import { ComicPanel } from "@/lib/types"; - -const ARK_API_URL = - "https://ark.cn-beijing.volces.com/api/v3/images/generations"; -const ARK_API_KEY = "7ef519e4-d564-4f90-8459-94b6ef973e25"; // 在实际生产环境中应使用环境变量 -const BASE_IMAGE_URL = - "https://ark-project.tos-cn-beijing.volces.com/doc_image/seededit_i2i.jpeg"; // 示例基础图像URL - -// 定义比例类型 -type AspectRatioKey = - | "1:1" - | "3:4" - | "4:3" - | "16:9" - | "9:16" - | "2:3" - | "3:2" - | "21:9" - | "custom"; - -// 将比例转换为图像尺寸的映射 -const aspectRatioToSize: Record = { - "1:1": "1024x1024", - "3:4": "768x1024", - "4:3": "1024x768", - "16:9": "1024x576", - "9:16": "576x1024", - "2:3": "768x1152", - "3:2": "1152x768", - "21:9": "1344x576", - custom: "1024x1024", // 默认为1:1 -}; - -export async function POST(request: NextRequest) { - try { - const { comicPanels, aspectRatio = "16:9" } = await request.json(); - - if ( - !comicPanels || - !Array.isArray(comicPanels) || - comicPanels.length === 0 - ) { - return NextResponse.json( - { error: "缺少有效的漫画剧本数据" }, - { status: 400 } - ); - } - - // 处理每个场景,生成对应的图像 - const imageGenerationPromises = comicPanels.map( - async (panel: ComicPanel, index: number) => { - try { - // 从剧本内容中提取关键信息,构建适合图像生成的提示词 - const prompt = `漫画风格的场景:${panel.content.substring(0, 200)}`; - - // 获取对应比例的尺寸 - const size = - aspectRatioToSize[aspectRatio as AspectRatioKey] || "1024x1024"; - - // 调用ARK图像生成API - const response = await fetch(ARK_API_URL, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${ARK_API_KEY}`, - }, - body: JSON.stringify({ - model: "doubao-seededit-3-0-i2i-250628", - prompt: prompt, - image: BASE_IMAGE_URL, - response_format: "url", - size: size, // 使用转换后的尺寸值 - seed: Math.floor(Math.random() * 1000), // 随机种子以获得不同的结果 - guidance_scale: 5.5, - watermark: true, - }), - }); - - if (!response.ok) { - const errorData = await response.text(); - console.error(`ARK API 错误 (场景 ${index + 1}):`, errorData); - throw new Error(`生成图像失败: ${response.statusText}`); - } - - const data = await response.json(); - return { - id: panel.id, - content: panel.content, - imageUrl: data.data?.[0]?.url || panel.imageUrl, // 如果有新图像URL则更新,否则保留原样 - sceneNumber: index + 1, - }; - } catch (err) { - console.error(`处理场景 ${index + 1} 时出错:`, err); - // 出错时返回原始面板数据,但添加错误信息 - return { - ...panel, - imageUrl: panel.imageUrl, - sceneNumber: index + 1, - error: err instanceof Error ? err.message : "图像生成失败", - }; - } - } - ); - - // 等待所有图像生成完成 - const results = await Promise.allSettled(imageGenerationPromises); - - // 处理结果 - const generatedPanels = results.map((result, index) => { - if (result.status === "fulfilled") { - return result.value; - } else { - // 如果某个请求失败,返回原始面板但带有错误信息 - return { - ...comicPanels[index], - sceneNumber: index + 1, - error: "图像生成请求失败", - }; - } - }); - - return NextResponse.json({ comicPanels: generatedPanels }); - } catch (error) { - console.error("图像生成API路由错误:", error); - return NextResponse.json({ error: "服务器内部错误" }, { status: 500 }); - } -} diff --git a/.history/app/api/generate-images/route_20250802000026.ts b/.history/app/api/generate-images/route_20250802000026.ts deleted file mode 100644 index c4bcf50..0000000 --- a/.history/app/api/generate-images/route_20250802000026.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; -import { ComicPanel } from "@/lib/types"; - -const ARK_API_URL = - "https://ark.cn-beijing.volces.com/api/v3/images/generations"; -const ARK_API_KEY = "7ef519e4-d564-4f90-8459-94b6ef973e25"; // 在实际生产环境中应使用环境变量 -const BASE_IMAGE_URL = - "https://ark-project.tos-cn-beijing.volces.com/doc_image/seededit_i2i.jpeg"; // 示例基础图像URL - -// 定义比例类型 -type AspectRatioKey = - | "1:1" - | "3:4" - | "4:3" - | "16:9" - | "9:16" - | "2:3" - | "3:2" - | "21:9" - | "custom"; - -// 将比例转换为图像尺寸的映射(使用API手册推荐的官方尺寸) -const aspectRatioToSize: Record = { - "1:1": "1024x1024", // (1:1) - "3:4": "864x1152", // (3:4) - "4:3": "1152x864", // (4:3) - "16:9": "1280x720", // (16:9) - "9:16": "720x1280", // (9:16) - "2:3": "832x1248", // (2:3) - "3:2": "1248x832", // (3:2) - "21:9": "1512x648", // (21:9) - custom: "1024x1024", // 默认为1:1 -}; - -export async function POST(request: NextRequest) { - try { - const { comicPanels, aspectRatio = "16:9" } = await request.json(); - - if ( - !comicPanels || - !Array.isArray(comicPanels) || - comicPanels.length === 0 - ) { - return NextResponse.json( - { error: "缺少有效的漫画剧本数据" }, - { status: 400 } - ); - } - - // 处理每个场景,生成对应的图像 - const imageGenerationPromises = comicPanels.map( - async (panel: ComicPanel, index: number) => { - try { - // 从剧本内容中提取关键信息,构建适合图像生成的提示词 - const prompt = `漫画风格的场景:${panel.content.substring(0, 200)}`; - - // 获取对应比例的尺寸 - const size = - aspectRatioToSize[aspectRatio as AspectRatioKey] || "1024x1024"; - - console.log( - `生成场景 ${index + 1}, 使用尺寸: ${size}, 比例: ${aspectRatio}` - ); - - // 调用ARK图像生成API - const response = await fetch(ARK_API_URL, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${ARK_API_KEY}`, - }, - body: JSON.stringify({ - model: "doubao-seededit-3-0-i2i-250628", - prompt: prompt, - image: BASE_IMAGE_URL, - response_format: "url", - size: size, // 使用API手册推荐的尺寸值 - seed: Math.floor(Math.random() * 1000), // 随机种子以获得不同的结果 - guidance_scale: 5.5, - watermark: true, - }), - }); - - if (!response.ok) { - const errorData = await response.text(); - console.error(`ARK API 错误 (场景 ${index + 1}):`, errorData); - throw new Error(`生成图像失败: ${response.statusText}`); - } - - const data = await response.json(); - return { - id: panel.id, - content: panel.content, - imageUrl: data.data?.[0]?.url || panel.imageUrl, // 如果有新图像URL则更新,否则保留原样 - sceneNumber: index + 1, - }; - } catch (err) { - console.error(`处理场景 ${index + 1} 时出错:`, err); - // 出错时返回原始面板数据,但添加错误信息 - return { - ...panel, - imageUrl: panel.imageUrl, - sceneNumber: index + 1, - error: err instanceof Error ? err.message : "图像生成失败", - }; - } - } - ); - - // 等待所有图像生成完成 - const results = await Promise.allSettled(imageGenerationPromises); - - // 处理结果 - const generatedPanels = results.map((result, index) => { - if (result.status === "fulfilled") { - return result.value; - } else { - // 如果某个请求失败,返回原始面板但带有错误信息 - return { - ...comicPanels[index], - sceneNumber: index + 1, - error: "图像生成请求失败", - }; - } - }); - - return NextResponse.json({ comicPanels: generatedPanels }); - } catch (error) { - console.error("图像生成API路由错误:", error); - return NextResponse.json({ error: "服务器内部错误" }, { status: 500 }); - } -} diff --git a/.history/app/api/generate-images/route_20250802000315.ts b/.history/app/api/generate-images/route_20250802000315.ts deleted file mode 100644 index 5e4f591..0000000 --- a/.history/app/api/generate-images/route_20250802000315.ts +++ /dev/null @@ -1,144 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; -import { ComicPanel } from "@/lib/types"; - -const ARK_API_URL = - "https://ark.cn-beijing.volces.com/api/v3/images/generations"; -const ARK_API_KEY = "7ef519e4-d564-4f90-8459-94b6ef973e25"; // 在实际生产环境中应使用环境变量 -const BASE_IMAGE_URL = - "https://ark-project.tos-cn-beijing.volces.com/doc_image/seededit_i2i.jpeg"; // 示例基础图像URL - -// 定义比例类型 -type AspectRatioKey = - | "1:1" - | "3:4" - | "4:3" - | "16:9" - | "9:16" - | "2:3" - | "3:2" - | "21:9" - | "custom"; - -// 将比例转换为图像尺寸的映射(使用API手册推荐的官方尺寸) -const aspectRatioToSize: Record< - AspectRatioKey, - { width: number; height: number } -> = { - "1:1": { width: 1024, height: 1024 }, // (1:1) - "3:4": { width: 864, height: 1152 }, // (3:4) - "4:3": { width: 1152, height: 864 }, // (4:3) - "16:9": { width: 1280, height: 720 }, // (16:9) - "9:16": { width: 720, height: 1280 }, // (9:16) - "2:3": { width: 832, height: 1248 }, // (2:3) - "3:2": { width: 1248, height: 832 }, // (3:2) - "21:9": { width: 1512, height: 648 }, // (21:9) - custom: { width: 1024, height: 1024 }, // 默认为1:1 -}; - -export async function POST(request: NextRequest) { - try { - const { comicPanels, aspectRatio = "16:9" } = await request.json(); - - if ( - !comicPanels || - !Array.isArray(comicPanels) || - comicPanels.length === 0 - ) { - return NextResponse.json( - { error: "缺少有效的漫画剧本数据" }, - { status: 400 } - ); - } - - // 处理每个场景,生成对应的图像 - const imageGenerationPromises = comicPanels.map( - async (panel: ComicPanel, index: number) => { - try { - // 从剧本内容中提取关键信息,构建适合图像生成的提示词 - const prompt = `漫画风格的场景:${panel.content.substring(0, 200)}`; - - // 获取对应比例的尺寸 - const dimensions = aspectRatioToSize[ - aspectRatio as AspectRatioKey - ] || { width: 1024, height: 1024 }; - - console.log( - `生成场景 ${index + 1}, 使用尺寸: ${dimensions.width}x${ - dimensions.height - }, 比例: ${aspectRatio}` - ); - - // 准备API请求体,注意确保完全按照API文档的要求 - const requestBody = { - model: "doubao-seededit-3-0-i2i-250628", - prompt: prompt, - image: BASE_IMAGE_URL, - response_format: "url", - width: dimensions.width, // 使用width和height分别指定宽高 - height: dimensions.height, - seed: Math.floor(Math.random() * 1000), // 随机种子以获得不同的结果 - guidance_scale: 5.5, - watermark: true, - }; - - console.log(`请求体: ${JSON.stringify(requestBody)}`); - - // 调用ARK图像生成API - const response = await fetch(ARK_API_URL, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${ARK_API_KEY}`, - }, - body: JSON.stringify(requestBody), - }); - - if (!response.ok) { - const errorData = await response.text(); - console.error(`ARK API 错误 (场景 ${index + 1}):`, errorData); - throw new Error(`生成图像失败: ${response.statusText}`); - } - - const data = await response.json(); - return { - id: panel.id, - content: panel.content, - imageUrl: data.data?.[0]?.url || panel.imageUrl, // 如果有新图像URL则更新,否则保留原样 - sceneNumber: index + 1, - }; - } catch (err) { - console.error(`处理场景 ${index + 1} 时出错:`, err); - // 出错时返回原始面板数据,但添加错误信息 - return { - ...panel, - imageUrl: panel.imageUrl, - sceneNumber: index + 1, - error: err instanceof Error ? err.message : "图像生成失败", - }; - } - } - ); - - // 等待所有图像生成完成 - const results = await Promise.allSettled(imageGenerationPromises); - - // 处理结果 - const generatedPanels = results.map((result, index) => { - if (result.status === "fulfilled") { - return result.value; - } else { - // 如果某个请求失败,返回原始面板但带有错误信息 - return { - ...comicPanels[index], - sceneNumber: index + 1, - error: "图像生成请求失败", - }; - } - }); - - return NextResponse.json({ comicPanels: generatedPanels }); - } catch (error) { - console.error("图像生成API路由错误:", error); - return NextResponse.json({ error: "服务器内部错误" }, { status: 500 }); - } -} diff --git a/.history/app/api/generate-images/route_20250802000905.ts b/.history/app/api/generate-images/route_20250802000905.ts deleted file mode 100644 index f42572c..0000000 --- a/.history/app/api/generate-images/route_20250802000905.ts +++ /dev/null @@ -1,144 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; -import { ComicPanel } from "@/lib/types"; - -const ARK_API_URL = - "https://ark.cn-beijing.volces.com/api/v3/images/generations"; -const ARK_API_KEY = "7ef519e4-d564-4f90-8459-94b6ef973e25"; // 在实际生产环境中应使用环境变量 -const BASE_IMAGE_URL = - "https://ark-project.tos-cn-beijing.volces.com/doc_image/seededit_i2i.jpeg"; // 示例基础图像URL - -// 定义比例类型 -type AspectRatioKey = - | "1:1" - | "3:4" - | "4:3" - | "16:9" - | "9:16" - | "2:3" - | "3:2" - | "21:9" - | "custom"; - -// 将比例转换为图像尺寸的映射(使用API手册推荐的官方尺寸) -const aspectRatioToSize: Record< - AspectRatioKey, - { width: number; height: number } -> = { - "1:1": { width: 1024, height: 1024 }, // (1:1) - "3:4": { width: 864, height: 1152 }, // (3:4) - "4:3": { width: 1152, height: 864 }, // (4:3) - "16:9": { width: 1280, height: 720 }, // (16:9) - "9:16": { width: 720, height: 1280 }, // (9:16) - "2:3": { width: 832, height: 1248 }, // (2:3) - "3:2": { width: 1248, height: 832 }, // (3:2) - "21:9": { width: 1512, height: 648 }, // (21:9) - custom: { width: 1024, height: 1024 }, // 默认为1:1 -}; - -export async function POST(request: NextRequest) { - try { - const { comicPanels, aspectRatio = "16:9" } = await request.json(); - - if ( - !comicPanels || - !Array.isArray(comicPanels) || - comicPanels.length === 0 - ) { - return NextResponse.json( - { error: "缺少有效的漫画剧本数据" }, - { status: 400 } - ); - } - - // 处理每个场景,生成对应的图像 - const imageGenerationPromises = comicPanels.map( - async (panel: ComicPanel, index: number) => { - try { - // 从剧本内容中提取关键信息,构建适合图像生成的提示词 - const prompt = `漫画风格的场景:${panel.content.substring(0, 200)}`; - - // 获取对应比例的尺寸 - const dimensions = aspectRatioToSize[ - aspectRatio as AspectRatioKey - ] || { width: 1024, height: 1024 }; - - console.log( - `生成场景 ${index + 1}, 使用尺寸: ${dimensions.width}x${ - dimensions.height - }, 比例: ${aspectRatio}` - ); - - // 准备API请求体,注意确保完全按照API文档的要求 - const requestBody = { - model: "doubao-seededit-3-0-i2i-250628", - prompt: prompt, - image: BASE_IMAGE_URL, - response_format: "url", - width: dimensions.width, // 使用width和height分别指定宽高 - height: dimensions.height, - seed: Math.floor(Math.random() * 1000), // 随机种子以获得不同的结果 - guidance_scale: 5.5, - watermark: false, // 移除水印 - }; - - console.log(`请求体: ${JSON.stringify(requestBody)}`); - - // 调用ARK图像生成API - const response = await fetch(ARK_API_URL, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${ARK_API_KEY}`, - }, - body: JSON.stringify(requestBody), - }); - - if (!response.ok) { - const errorData = await response.text(); - console.error(`ARK API 错误 (场景 ${index + 1}):`, errorData); - throw new Error(`生成图像失败: ${response.statusText}`); - } - - const data = await response.json(); - return { - id: panel.id, - content: panel.content, - imageUrl: data.data?.[0]?.url || panel.imageUrl, // 如果有新图像URL则更新,否则保留原样 - sceneNumber: index + 1, - }; - } catch (err) { - console.error(`处理场景 ${index + 1} 时出错:`, err); - // 出错时返回原始面板数据,但添加错误信息 - return { - ...panel, - imageUrl: panel.imageUrl, - sceneNumber: index + 1, - error: err instanceof Error ? err.message : "图像生成失败", - }; - } - } - ); - - // 等待所有图像生成完成 - const results = await Promise.allSettled(imageGenerationPromises); - - // 处理结果 - const generatedPanels = results.map((result, index) => { - if (result.status === "fulfilled") { - return result.value; - } else { - // 如果某个请求失败,返回原始面板但带有错误信息 - return { - ...comicPanels[index], - sceneNumber: index + 1, - error: "图像生成请求失败", - }; - } - }); - - return NextResponse.json({ comicPanels: generatedPanels }); - } catch (error) { - console.error("图像生成API路由错误:", error); - return NextResponse.json({ error: "服务器内部错误" }, { status: 500 }); - } -} diff --git a/.history/app/api/generate-images/route_20250802001950.ts b/.history/app/api/generate-images/route_20250802001950.ts deleted file mode 100644 index 624b4f6..0000000 --- a/.history/app/api/generate-images/route_20250802001950.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; -import { ComicPanel } from "@/lib/types"; - -const ARK_API_URL = - "https://ark.cn-beijing.volces.com/api/v3/images/generations"; -const ARK_API_KEY = "7ef519e4-d564-4f90-8459-94b6ef973e25"; // 在实际生产环境中应使用环境变量 -const BASE_IMAGE_URL = - "https://ark-project.tos-cn-beijing.volces.com/doc_image/seededit_i2i.jpeg"; // 示例基础图像URL - -// 定义比例类型 -type AspectRatioKey = - | "1:1" - | "3:4" - | "4:3" - | "16:9" - | "9:16" - | "2:3" - | "3:2" - | "21:9" - | "custom"; - -// 将比例转换为图像尺寸的映射(使用API手册推荐的官方尺寸) -const aspectRatioToSize: Record< - AspectRatioKey, - { width: number; height: number } -> = { - "1:1": { width: 1024, height: 1024 }, // (1:1) - "3:4": { width: 864, height: 1152 }, // (3:4) - "4:3": { width: 1152, height: 864 }, // (4:3) - "16:9": { width: 1280, height: 720 }, // (16:9) - "9:16": { width: 720, height: 1280 }, // (9:16) - "2:3": { width: 832, height: 1248 }, // (2:3) - "3:2": { width: 1248, height: 832 }, // (3:2) - "21:9": { width: 1512, height: 648 }, // (21:9) - custom: { width: 1024, height: 1024 }, // 默认为1:1 -}; - -export async function POST(request: NextRequest) { - try { - const { - comicPanels, - aspectRatio = "16:9", - seed = -1, - guidance_scale = 2.5, - } = await request.json(); - - if ( - !comicPanels || - !Array.isArray(comicPanels) || - comicPanels.length === 0 - ) { - return NextResponse.json( - { error: "缺少有效的漫画剧本数据" }, - { status: 400 } - ); - } - - // 处理每个场景,生成对应的图像 - const imageGenerationPromises = comicPanels.map( - async (panel: ComicPanel, index: number) => { - try { - // 从剧本内容中提取关键信息,构建适合图像生成的提示词 - const prompt = `漫画风格的场景:${panel.content.substring(0, 200)}`; - - // 获取对应比例的尺寸 - const dimensions = aspectRatioToSize[ - aspectRatio as AspectRatioKey - ] || { width: 1024, height: 1024 }; - - // 为每个面板生成不同的种子,如果用户选择了固定种子,则所有面板使用相同的种子 - const panelSeed = - seed === -1 ? Math.floor(Math.random() * 2147483647) : seed; - - console.log( - `生成场景 ${index + 1}, 使用尺寸: ${dimensions.width}x${ - dimensions.height - }, 比例: ${aspectRatio}, 种子: ${panelSeed}, 文本权重: ${guidance_scale}` - ); - - // 准备API请求体,注意确保完全按照API文档的要求 - const requestBody = { - model: "doubao-seededit-3-0-i2i-250628", - prompt: prompt, - image: BASE_IMAGE_URL, - response_format: "url", - width: dimensions.width, // 使用width和height分别指定宽高 - height: dimensions.height, - seed: panelSeed, // 使用计算得到的种子值 - guidance_scale: guidance_scale, // 使用传入的文本权重值 - watermark: false, // 移除水印 - }; - - console.log(`请求体: ${JSON.stringify(requestBody)}`); - - // 调用ARK图像生成API - const response = await fetch(ARK_API_URL, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${ARK_API_KEY}`, - }, - body: JSON.stringify(requestBody), - }); - - if (!response.ok) { - const errorData = await response.text(); - console.error(`ARK API 错误 (场景 ${index + 1}):`, errorData); - throw new Error(`生成图像失败: ${response.statusText}`); - } - - const data = await response.json(); - return { - id: panel.id, - content: panel.content, - imageUrl: data.data?.[0]?.url || panel.imageUrl, // 如果有新图像URL则更新,否则保留原样 - sceneNumber: index + 1, - }; - } catch (err) { - console.error(`处理场景 ${index + 1} 时出错:`, err); - // 出错时返回原始面板数据,但添加错误信息 - return { - ...panel, - imageUrl: panel.imageUrl, - sceneNumber: index + 1, - error: err instanceof Error ? err.message : "图像生成失败", - }; - } - } - ); - - // 等待所有图像生成完成 - const results = await Promise.allSettled(imageGenerationPromises); - - // 处理结果 - const generatedPanels = results.map((result, index) => { - if (result.status === "fulfilled") { - return result.value; - } else { - // 如果某个请求失败,返回原始面板但带有错误信息 - return { - ...comicPanels[index], - sceneNumber: index + 1, - error: "图像生成请求失败", - }; - } - }); - - return NextResponse.json({ comicPanels: generatedPanels }); - } catch (error) { - console.error("图像生成API路由错误:", error); - return NextResponse.json({ error: "服务器内部错误" }, { status: 500 }); - } -} diff --git a/.history/app/api/generate-images/route_20250802003058.ts b/.history/app/api/generate-images/route_20250802003058.ts deleted file mode 100644 index 624b4f6..0000000 --- a/.history/app/api/generate-images/route_20250802003058.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; -import { ComicPanel } from "@/lib/types"; - -const ARK_API_URL = - "https://ark.cn-beijing.volces.com/api/v3/images/generations"; -const ARK_API_KEY = "7ef519e4-d564-4f90-8459-94b6ef973e25"; // 在实际生产环境中应使用环境变量 -const BASE_IMAGE_URL = - "https://ark-project.tos-cn-beijing.volces.com/doc_image/seededit_i2i.jpeg"; // 示例基础图像URL - -// 定义比例类型 -type AspectRatioKey = - | "1:1" - | "3:4" - | "4:3" - | "16:9" - | "9:16" - | "2:3" - | "3:2" - | "21:9" - | "custom"; - -// 将比例转换为图像尺寸的映射(使用API手册推荐的官方尺寸) -const aspectRatioToSize: Record< - AspectRatioKey, - { width: number; height: number } -> = { - "1:1": { width: 1024, height: 1024 }, // (1:1) - "3:4": { width: 864, height: 1152 }, // (3:4) - "4:3": { width: 1152, height: 864 }, // (4:3) - "16:9": { width: 1280, height: 720 }, // (16:9) - "9:16": { width: 720, height: 1280 }, // (9:16) - "2:3": { width: 832, height: 1248 }, // (2:3) - "3:2": { width: 1248, height: 832 }, // (3:2) - "21:9": { width: 1512, height: 648 }, // (21:9) - custom: { width: 1024, height: 1024 }, // 默认为1:1 -}; - -export async function POST(request: NextRequest) { - try { - const { - comicPanels, - aspectRatio = "16:9", - seed = -1, - guidance_scale = 2.5, - } = await request.json(); - - if ( - !comicPanels || - !Array.isArray(comicPanels) || - comicPanels.length === 0 - ) { - return NextResponse.json( - { error: "缺少有效的漫画剧本数据" }, - { status: 400 } - ); - } - - // 处理每个场景,生成对应的图像 - const imageGenerationPromises = comicPanels.map( - async (panel: ComicPanel, index: number) => { - try { - // 从剧本内容中提取关键信息,构建适合图像生成的提示词 - const prompt = `漫画风格的场景:${panel.content.substring(0, 200)}`; - - // 获取对应比例的尺寸 - const dimensions = aspectRatioToSize[ - aspectRatio as AspectRatioKey - ] || { width: 1024, height: 1024 }; - - // 为每个面板生成不同的种子,如果用户选择了固定种子,则所有面板使用相同的种子 - const panelSeed = - seed === -1 ? Math.floor(Math.random() * 2147483647) : seed; - - console.log( - `生成场景 ${index + 1}, 使用尺寸: ${dimensions.width}x${ - dimensions.height - }, 比例: ${aspectRatio}, 种子: ${panelSeed}, 文本权重: ${guidance_scale}` - ); - - // 准备API请求体,注意确保完全按照API文档的要求 - const requestBody = { - model: "doubao-seededit-3-0-i2i-250628", - prompt: prompt, - image: BASE_IMAGE_URL, - response_format: "url", - width: dimensions.width, // 使用width和height分别指定宽高 - height: dimensions.height, - seed: panelSeed, // 使用计算得到的种子值 - guidance_scale: guidance_scale, // 使用传入的文本权重值 - watermark: false, // 移除水印 - }; - - console.log(`请求体: ${JSON.stringify(requestBody)}`); - - // 调用ARK图像生成API - const response = await fetch(ARK_API_URL, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${ARK_API_KEY}`, - }, - body: JSON.stringify(requestBody), - }); - - if (!response.ok) { - const errorData = await response.text(); - console.error(`ARK API 错误 (场景 ${index + 1}):`, errorData); - throw new Error(`生成图像失败: ${response.statusText}`); - } - - const data = await response.json(); - return { - id: panel.id, - content: panel.content, - imageUrl: data.data?.[0]?.url || panel.imageUrl, // 如果有新图像URL则更新,否则保留原样 - sceneNumber: index + 1, - }; - } catch (err) { - console.error(`处理场景 ${index + 1} 时出错:`, err); - // 出错时返回原始面板数据,但添加错误信息 - return { - ...panel, - imageUrl: panel.imageUrl, - sceneNumber: index + 1, - error: err instanceof Error ? err.message : "图像生成失败", - }; - } - } - ); - - // 等待所有图像生成完成 - const results = await Promise.allSettled(imageGenerationPromises); - - // 处理结果 - const generatedPanels = results.map((result, index) => { - if (result.status === "fulfilled") { - return result.value; - } else { - // 如果某个请求失败,返回原始面板但带有错误信息 - return { - ...comicPanels[index], - sceneNumber: index + 1, - error: "图像生成请求失败", - }; - } - }); - - return NextResponse.json({ comicPanels: generatedPanels }); - } catch (error) { - console.error("图像生成API路由错误:", error); - return NextResponse.json({ error: "服务器内部错误" }, { status: 500 }); - } -} diff --git a/.history/app/api/generate-images/route_20250802004958.ts b/.history/app/api/generate-images/route_20250802004958.ts deleted file mode 100644 index 624b4f6..0000000 --- a/.history/app/api/generate-images/route_20250802004958.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; -import { ComicPanel } from "@/lib/types"; - -const ARK_API_URL = - "https://ark.cn-beijing.volces.com/api/v3/images/generations"; -const ARK_API_KEY = "7ef519e4-d564-4f90-8459-94b6ef973e25"; // 在实际生产环境中应使用环境变量 -const BASE_IMAGE_URL = - "https://ark-project.tos-cn-beijing.volces.com/doc_image/seededit_i2i.jpeg"; // 示例基础图像URL - -// 定义比例类型 -type AspectRatioKey = - | "1:1" - | "3:4" - | "4:3" - | "16:9" - | "9:16" - | "2:3" - | "3:2" - | "21:9" - | "custom"; - -// 将比例转换为图像尺寸的映射(使用API手册推荐的官方尺寸) -const aspectRatioToSize: Record< - AspectRatioKey, - { width: number; height: number } -> = { - "1:1": { width: 1024, height: 1024 }, // (1:1) - "3:4": { width: 864, height: 1152 }, // (3:4) - "4:3": { width: 1152, height: 864 }, // (4:3) - "16:9": { width: 1280, height: 720 }, // (16:9) - "9:16": { width: 720, height: 1280 }, // (9:16) - "2:3": { width: 832, height: 1248 }, // (2:3) - "3:2": { width: 1248, height: 832 }, // (3:2) - "21:9": { width: 1512, height: 648 }, // (21:9) - custom: { width: 1024, height: 1024 }, // 默认为1:1 -}; - -export async function POST(request: NextRequest) { - try { - const { - comicPanels, - aspectRatio = "16:9", - seed = -1, - guidance_scale = 2.5, - } = await request.json(); - - if ( - !comicPanels || - !Array.isArray(comicPanels) || - comicPanels.length === 0 - ) { - return NextResponse.json( - { error: "缺少有效的漫画剧本数据" }, - { status: 400 } - ); - } - - // 处理每个场景,生成对应的图像 - const imageGenerationPromises = comicPanels.map( - async (panel: ComicPanel, index: number) => { - try { - // 从剧本内容中提取关键信息,构建适合图像生成的提示词 - const prompt = `漫画风格的场景:${panel.content.substring(0, 200)}`; - - // 获取对应比例的尺寸 - const dimensions = aspectRatioToSize[ - aspectRatio as AspectRatioKey - ] || { width: 1024, height: 1024 }; - - // 为每个面板生成不同的种子,如果用户选择了固定种子,则所有面板使用相同的种子 - const panelSeed = - seed === -1 ? Math.floor(Math.random() * 2147483647) : seed; - - console.log( - `生成场景 ${index + 1}, 使用尺寸: ${dimensions.width}x${ - dimensions.height - }, 比例: ${aspectRatio}, 种子: ${panelSeed}, 文本权重: ${guidance_scale}` - ); - - // 准备API请求体,注意确保完全按照API文档的要求 - const requestBody = { - model: "doubao-seededit-3-0-i2i-250628", - prompt: prompt, - image: BASE_IMAGE_URL, - response_format: "url", - width: dimensions.width, // 使用width和height分别指定宽高 - height: dimensions.height, - seed: panelSeed, // 使用计算得到的种子值 - guidance_scale: guidance_scale, // 使用传入的文本权重值 - watermark: false, // 移除水印 - }; - - console.log(`请求体: ${JSON.stringify(requestBody)}`); - - // 调用ARK图像生成API - const response = await fetch(ARK_API_URL, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${ARK_API_KEY}`, - }, - body: JSON.stringify(requestBody), - }); - - if (!response.ok) { - const errorData = await response.text(); - console.error(`ARK API 错误 (场景 ${index + 1}):`, errorData); - throw new Error(`生成图像失败: ${response.statusText}`); - } - - const data = await response.json(); - return { - id: panel.id, - content: panel.content, - imageUrl: data.data?.[0]?.url || panel.imageUrl, // 如果有新图像URL则更新,否则保留原样 - sceneNumber: index + 1, - }; - } catch (err) { - console.error(`处理场景 ${index + 1} 时出错:`, err); - // 出错时返回原始面板数据,但添加错误信息 - return { - ...panel, - imageUrl: panel.imageUrl, - sceneNumber: index + 1, - error: err instanceof Error ? err.message : "图像生成失败", - }; - } - } - ); - - // 等待所有图像生成完成 - const results = await Promise.allSettled(imageGenerationPromises); - - // 处理结果 - const generatedPanels = results.map((result, index) => { - if (result.status === "fulfilled") { - return result.value; - } else { - // 如果某个请求失败,返回原始面板但带有错误信息 - return { - ...comicPanels[index], - sceneNumber: index + 1, - error: "图像生成请求失败", - }; - } - }); - - return NextResponse.json({ comicPanels: generatedPanels }); - } catch (error) { - console.error("图像生成API路由错误:", error); - return NextResponse.json({ error: "服务器内部错误" }, { status: 500 }); - } -} diff --git a/.history/app/api/generate/route_20250801230209.ts b/.history/app/api/generate/route_20250801230209.ts deleted file mode 100644 index 0519ecb..0000000 --- a/.history/app/api/generate/route_20250801230209.ts +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/.history/app/api/generate/route_20250801230215.ts b/.history/app/api/generate/route_20250801230215.ts deleted file mode 100644 index 7ad054f..0000000 --- a/.history/app/api/generate/route_20250801230215.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; - -const DEEPSEEK_API_URL = "https://api.siliconflow.cn/v1/chat/completions"; -const DEEPSEEK_API_KEY = "sk-xgpdmqrgikwkbaacvxbvtkwhxnwjncdvqzlshnzcyswxhsfn"; // 在实际生产环境中应使用环境变量 - -export async function POST(request: NextRequest) { - try { - const { content, count = 4 } = await request.json(); - - if (!content || content.trim() === "") { - return NextResponse.json({ error: "文章内容不能为空" }, { status: 400 }); - } - - // 构建提示词,要求生成漫画面板的描述 - const prompt = ` -请根据以下故事情节,创作${count}幅漫画场景的详细视觉描述。 -每幅描述应包含场景、角色、动作、表情和背景等细节,以便用于生成漫画面板。 -请确保描述具有连贯性,能够讲述完整的故事。 -每幅描述以 "场景1:"、"场景2:" 等格式开头,并用换行分隔。 -只输出场景描述,不要有其他解释性文字。 - -故事:${content} -`; - - // 调用DeepSeek API - const response = await fetch(DEEPSEEK_API_URL, { - method: "POST", - headers: { - Authorization: `Bearer ${DEEPSEEK_API_KEY}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - model: "Qwen/QwQ-32B", // 使用清华的Qwen模型生成文本描述 - messages: [ - { - role: "user", - content: prompt, - }, - ], - temperature: 0.7, - }), - }); - - if (!response.ok) { - const errorData = await response.text(); - console.error("DeepSeek API错误:", errorData); - return NextResponse.json( - { error: "生成漫画描述失败" }, - { status: response.status } - ); - } - - const data = await response.json(); - const descriptions = data.choices[0].message.content.trim(); - - // 将文本描述转换为场景数组 - const scenes = descriptions - .split(/场景\d+:/g) - .filter(Boolean) - .map((scene) => scene.trim()) - .slice(0, count); - - // 为每个场景生成假的图片URL (实际项目中可以接入图像生成API) - const comicPanels = scenes.map((description, index) => ({ - id: `panel-${index + 1}`, - description: description, - // 在实际项目中,这里应该调用图像生成API,目前用占位图 - imageUrl: `https://placehold.co/600x400/pink/white?text=Comic+${ - index + 1 - }`, - })); - - return NextResponse.json({ comicPanels }); - } catch (error) { - console.error("API路由错误:", error); - return NextResponse.json({ error: "服务器内部错误" }, { status: 500 }); - } -} diff --git a/.history/app/api/generate/route_20250801231240.ts b/.history/app/api/generate/route_20250801231240.ts deleted file mode 100644 index b93fed0..0000000 --- a/.history/app/api/generate/route_20250801231240.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; - -const DEEPSEEK_API_URL = "https://api.siliconflow.cn/v1/chat/completions"; -const DEEPSEEK_API_KEY = "sk-xgpdmqrgikwkbaacvxbvtkwhxnwjncdvqzlshnzcyswxhsfn"; // 在实际生产环境中应使用环境变量 - -export async function POST(request: NextRequest) { - try { - const { content, count = 4 } = await request.json(); - - if (!content || content.trim() === "") { - return NextResponse.json({ error: "文章内容不能为空" }, { status: 400 }); - } - - // 构建提示词,要求创作完整的漫画故事 - const prompt = ` -你是一位专业的漫画脚本创作者。请根据以下内容,创作一个完整的漫画故事,分为${count}个场景。 - -每个场景需要包含以下元素: -1. 场景描述:包括地点、时间、环境等 -2. 角色描述:包括外貌、表情、动作、姿态等 -3. 对话内容:角色之间的对话或内心独白 -4. 情感氛围:该场景的主要情感基调 - -请确保故事具有连贯性和完整的情节发展,包括开始、发展、高潮和结局。 -每个场景以"【场景1】"、"【场景2】"等格式开头,并用明显的分隔符分开。 - -用户提供的内容或创意: -${content} - -请基于以上内容进行创作,不要有任何与创作无关的解释性文字。直接输出创作内容。 -`; - - // 调用DeepSeek API - const response = await fetch(DEEPSEEK_API_URL, { - method: "POST", - headers: { - Authorization: `Bearer ${DEEPSEEK_API_KEY}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - model: "Qwen/QwQ-32B", // 使用清华的Qwen模型生成漫画故事 - messages: [ - { - role: "user", - content: prompt, - }, - ], - temperature: 0.8, // 略微提高创意性 - max_tokens: 2000, // 确保有足够的输出长度 - }), - }); - - if (!response.ok) { - const errorData = await response.text(); - console.error("DeepSeek API错误:", errorData); - return NextResponse.json( - { error: "生成漫画故事失败" }, - { status: response.status } - ); - } - - const data = await response.json(); - const storyContent = data.choices[0].message.content.trim(); - - // 解析故事内容,提取各个场景 - const scenes = storyContent - .split(/【场景\d+】/g) - .filter(Boolean) - .map((scene) => scene.trim()) - .slice(0, count); - - // 为每个场景生成假的图片URL (实际项目中可以接入图像生成API) - const comicPanels = scenes.map((content, index) => ({ - id: `panel-${index + 1}`, - content: content, - // 在实际项目中,这里应该调用图像生成API,目前用占位图 - imageUrl: `https://placehold.co/600x400/pink/white?text=Comic+${ - index + 1 - }`, - })); - - return NextResponse.json({ comicPanels }); - } catch (error) { - console.error("API路由错误:", error); - return NextResponse.json({ error: "服务器内部错误" }, { status: 500 }); - } -} diff --git a/.history/app/api/generate/route_20250801231305.ts b/.history/app/api/generate/route_20250801231305.ts deleted file mode 100644 index 263f98d..0000000 --- a/.history/app/api/generate/route_20250801231305.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; - -const DEEPSEEK_API_URL = "https://api.siliconflow.cn/v1/chat/completions"; -const DEEPSEEK_API_KEY = "sk-xgpdmqrgikwkbaacvxbvtkwhxnwjncdvqzlshnzcyswxhsfn"; // 在实际生产环境中应使用环境变量 - -export async function POST(request: NextRequest) { - try { - const { content, count = 4 } = await request.json(); - - if (!content || content.trim() === "") { - return NextResponse.json({ error: "文章内容不能为空" }, { status: 400 }); - } - - // 构建提示词,要求创作完整的漫画故事 - const prompt = ` -你是一位专业的漫画脚本创作者。请根据以下内容,创作一个完整的漫画故事,分为${count}个场景。 - -每个场景需要包含以下元素: -1. 场景描述:包括地点、时间、环境等 -2. 角色描述:包括外貌、表情、动作、姿态等 -3. 对话内容:角色之间的对话或内心独白 -4. 情感氛围:该场景的主要情感基调 - -请确保故事具有连贯性和完整的情节发展,包括开始、发展、高潮和结局。 -每个场景以"【场景1】"、"【场景2】"等格式开头,并用明显的分隔符分开。 - -用户提供的内容或创意: -${content} - -请基于以上内容进行创作,不要有任何与创作无关的解释性文字。直接输出创作内容。 -`; - - // 调用DeepSeek API - const response = await fetch(DEEPSEEK_API_URL, { - method: "POST", - headers: { - Authorization: `Bearer ${DEEPSEEK_API_KEY}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - model: "Qwen/QwQ-32B", // 使用清华的Qwen模型生成漫画故事 - messages: [ - { - role: "user", - content: prompt, - }, - ], - temperature: 0.8, // 略微提高创意性 - max_tokens: 2000, // 确保有足够的输出长度 - }), - }); - - if (!response.ok) { - const errorData = await response.text(); - console.error("DeepSeek API错误:", errorData); - return NextResponse.json( - { error: "生成漫画故事失败" }, - { status: response.status } - ); - } - - const data = await response.json(); - const storyContent = data.choices[0].message.content.trim(); - - // 解析故事内容,提取各个场景 - const scenes = storyContent - .split(/【场景\d+】/g) - .filter(Boolean) - .map((scene: string) => scene.trim()) - .slice(0, count); - - // 为每个场景生成假的图片URL (实际项目中可以接入图像生成API) - const comicPanels = scenes.map((content: string, index: number) => ({ - id: `panel-${index + 1}`, - content: content, - // 在实际项目中,这里应该调用图像生成API,目前用占位图 - imageUrl: `https://placehold.co/600x400/pink/white?text=Comic+${ - index + 1 - }`, - })); - - return NextResponse.json({ comicPanels }); - } catch (error) { - console.error("API路由错误:", error); - return NextResponse.json({ error: "服务器内部错误" }, { status: 500 }); - } -} diff --git a/.history/app/api/generate/route_20250801233240.ts b/.history/app/api/generate/route_20250801233240.ts deleted file mode 100644 index 0d0a52b..0000000 --- a/.history/app/api/generate/route_20250801233240.ts +++ /dev/null @@ -1,116 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; - -const DEEPSEEK_API_URL = "https://api.siliconflow.cn/v1/chat/completions"; -const DEEPSEEK_API_KEY = "sk-xgpdmqrgikwkbaacvxbvtkwhxnwjncdvqzlshnzcyswxhsfn"; // 在实际生产环境中应使用环境变量 - -export async function POST(request: NextRequest) { - try { - const { content, count = 4, sceneNumber } = await request.json(); - - if (!content || content.trim() === "") { - return NextResponse.json({ error: "文章内容不能为空" }, { status: 400 }); - } - - // 构建提示词,要求创作完整的漫画故事或单个场景 - let prompt = ""; - - if (sceneNumber) { - // 单个场景的提示词 - prompt = ` -你是一位专业的漫画脚本创作者。请根据以下内容,创作漫画故事的第${sceneNumber}个场景。 - -场景需要包含以下元素: -1. 场景描述:包括地点、时间、环境等 -2. 角色描述:包括外貌、表情、动作、姿态等 -3. 对话内容:角色之间的对话或内心独白 -4. 情感氛围:该场景的主要情感基调 - -用户提供的内容或创意: -${content} - -请基于以上内容进行创作,不要有任何与创作无关的解释性文字。直接输出创作内容。 -`; - } else { - // 完整故事的提示词 - prompt = ` -你是一位专业的漫画脚本创作者。请根据以下内容,创作一个完整的漫画故事,分为${count}个场景。 - -每个场景需要包含以下元素: -1. 场景描述:包括地点、时间、环境等 -2. 角色描述:包括外貌、表情、动作、姿态等 -3. 对话内容:角色之间的对话或内心独白 -4. 情感氛围:该场景的主要情感基调 - -请确保故事具有连贯性和完整的情节发展,包括开始、发展、高潮和结局。 -每个场景以"【场景1】"、"【场景2】"等格式开头,并用明显的分隔符分开。 - -用户提供的内容或创意: -${content} - -请基于以上内容进行创作,不要有任何与创作无关的解释性文字。直接输出创作内容。 -`; - } - - // 调用DeepSeek API - const response = await fetch(DEEPSEEK_API_URL, { - method: "POST", - headers: { - Authorization: `Bearer ${DEEPSEEK_API_KEY}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - model: "Qwen/QwQ-32B", // 使用清华的Qwen模型生成漫画故事 - messages: [ - { - role: "user", - content: prompt, - }, - ], - temperature: 0.8, // 略微提高创意性 - max_tokens: 2000, // 确保有足够的输出长度 - }), - }); - - if (!response.ok) { - const errorData = await response.text(); - console.error("DeepSeek API错误:", errorData); - return NextResponse.json( - { error: "生成漫画故事失败" }, - { status: response.status } - ); - } - - const data = await response.json(); - const storyContent = data.choices[0].message.content.trim(); - - // 解析故事内容 - let scenes: string[] = []; - - if (sceneNumber) { - // 单场景刷新,直接使用返回内容 - scenes = [storyContent]; - } else { - // 多场景生成,解析各个场景 - scenes = storyContent - .split(/【场景\d+】/g) - .filter(Boolean) - .map((scene: string) => scene.trim()) - .slice(0, count); - } - - // 为每个场景生成假的图片URL (实际项目中可以接入图像生成API) - const comicPanels = scenes.map((content: string, index: number) => ({ - id: sceneNumber ? `panel-${sceneNumber}` : `panel-${index + 1}`, - content: content, - // 在实际项目中,这里应该调用图像生成API,目前用占位图 - imageUrl: `https://placehold.co/600x400/pink/white?text=Comic+${ - sceneNumber || index + 1 - }`, - })); - - return NextResponse.json({ comicPanels }); - } catch (error) { - console.error("API路由错误:", error); - return NextResponse.json({ error: "服务器内部错误" }, { status: 500 }); - } -} diff --git a/.history/app/api/generate/route_20250801233434.ts b/.history/app/api/generate/route_20250801233434.ts deleted file mode 100644 index 263f98d..0000000 --- a/.history/app/api/generate/route_20250801233434.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; - -const DEEPSEEK_API_URL = "https://api.siliconflow.cn/v1/chat/completions"; -const DEEPSEEK_API_KEY = "sk-xgpdmqrgikwkbaacvxbvtkwhxnwjncdvqzlshnzcyswxhsfn"; // 在实际生产环境中应使用环境变量 - -export async function POST(request: NextRequest) { - try { - const { content, count = 4 } = await request.json(); - - if (!content || content.trim() === "") { - return NextResponse.json({ error: "文章内容不能为空" }, { status: 400 }); - } - - // 构建提示词,要求创作完整的漫画故事 - const prompt = ` -你是一位专业的漫画脚本创作者。请根据以下内容,创作一个完整的漫画故事,分为${count}个场景。 - -每个场景需要包含以下元素: -1. 场景描述:包括地点、时间、环境等 -2. 角色描述:包括外貌、表情、动作、姿态等 -3. 对话内容:角色之间的对话或内心独白 -4. 情感氛围:该场景的主要情感基调 - -请确保故事具有连贯性和完整的情节发展,包括开始、发展、高潮和结局。 -每个场景以"【场景1】"、"【场景2】"等格式开头,并用明显的分隔符分开。 - -用户提供的内容或创意: -${content} - -请基于以上内容进行创作,不要有任何与创作无关的解释性文字。直接输出创作内容。 -`; - - // 调用DeepSeek API - const response = await fetch(DEEPSEEK_API_URL, { - method: "POST", - headers: { - Authorization: `Bearer ${DEEPSEEK_API_KEY}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - model: "Qwen/QwQ-32B", // 使用清华的Qwen模型生成漫画故事 - messages: [ - { - role: "user", - content: prompt, - }, - ], - temperature: 0.8, // 略微提高创意性 - max_tokens: 2000, // 确保有足够的输出长度 - }), - }); - - if (!response.ok) { - const errorData = await response.text(); - console.error("DeepSeek API错误:", errorData); - return NextResponse.json( - { error: "生成漫画故事失败" }, - { status: response.status } - ); - } - - const data = await response.json(); - const storyContent = data.choices[0].message.content.trim(); - - // 解析故事内容,提取各个场景 - const scenes = storyContent - .split(/【场景\d+】/g) - .filter(Boolean) - .map((scene: string) => scene.trim()) - .slice(0, count); - - // 为每个场景生成假的图片URL (实际项目中可以接入图像生成API) - const comicPanels = scenes.map((content: string, index: number) => ({ - id: `panel-${index + 1}`, - content: content, - // 在实际项目中,这里应该调用图像生成API,目前用占位图 - imageUrl: `https://placehold.co/600x400/pink/white?text=Comic+${ - index + 1 - }`, - })); - - return NextResponse.json({ comicPanels }); - } catch (error) { - console.error("API路由错误:", error); - return NextResponse.json({ error: "服务器内部错误" }, { status: 500 }); - } -} diff --git a/.history/app/api/generate/route_20250802003341.ts b/.history/app/api/generate/route_20250802003341.ts deleted file mode 100644 index f09ba2b..0000000 --- a/.history/app/api/generate/route_20250802003341.ts +++ /dev/null @@ -1,127 +0,0 @@ -import { NextResponse } from "next/server"; -import { ComicPanel } from "@/lib/types"; - -// 支持的模型列表 -const SUPPORTED_MODELS = { - "qwen-32b": "Qwen/QwQ-32B", - "deepseek-7b": "deepseek-ai/deepseek-coder-7b-instruct", // 示例模型ID - "gpt-3.5": "gpt-3.5-turbo", // 示例模型ID -}; - -export async function POST(request: Request) { - try { - const { - content, - count, - sceneNumber, - model = "qwen-32b", - } = await request.json(); - - if (!content) { - return NextResponse.json({ error: "内容不能为空" }, { status: 400 }); - } - - // 验证模型是否支持 - if (!SUPPORTED_MODELS[model]) { - return NextResponse.json({ error: "不支持的模型" }, { status: 400 }); - } - - // 模型ID - const modelId = SUPPORTED_MODELS[model]; - - // 构建提示词 - let prompt; - - // 如果指定了场景编号,则只生成该场景 - if (typeof sceneNumber === "number") { - prompt = `请你根据以下内容生成一个漫画场景的描述,该场景是第${sceneNumber}个场景,生成的漫画描述应该简洁、具体且富有视觉感。 - - 内容: ${content} - - 请直接给出第${sceneNumber}个场景的描述,不要有多余的解释。`; - } else { - // 否则生成多个场景 - prompt = `请你根据以下内容生成${count}个漫画场景的描述,每个场景的描述应该简洁、具体且富有视觉感。 - - 内容: ${content} - - 请直接给出${count}个场景的描述,每个场景单独一段,格式如下: - 场景1:[场景1的描述] - 场景2:[场景2的描述] - ... - - 不要有多余的解释。`; - } - - // 调用 DeepSeek API - const response = await fetch( - "https://api.deepseek.com/v1/chat/completions", - { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${process.env.DEEPSEEK_API_KEY}`, - }, - body: JSON.stringify({ - model: modelId, - messages: [ - { - role: "user", - content: prompt, - }, - ], - temperature: 0.7, - max_tokens: 1000, - }), - } - ); - - if (!response.ok) { - const errorData = await response.json(); - console.error("DeepSeek API错误:", errorData); - return NextResponse.json( - { error: "调用AI服务失败,请稍后再试" }, - { status: 500 } - ); - } - - const responseData = await response.json(); - const generatedText = responseData.choices[0].message.content; - - console.log("生成的内容:", generatedText); - - let comicPanels: ComicPanel[] = []; - - // 处理单个场景刷新的情况 - if (typeof sceneNumber === "number") { - comicPanels = [ - { - id: `scene-${sceneNumber}`, - content: generatedText.trim(), - imageUrl: `https://placehold.co/600x400?text=场景${sceneNumber}`, - sceneNumber: sceneNumber, - }, - ]; - } else { - // 处理多个场景的情况 - const scenes = generatedText - .split(/场景\d+[::]/) - .filter((scene: string) => scene.trim().length > 0); - - comicPanels = scenes.map((scene: string, index: number) => ({ - id: `scene-${index + 1}`, - content: scene.trim(), - imageUrl: `https://placehold.co/600x400?text=场景${index + 1}`, - sceneNumber: index + 1, - })); - } - - return NextResponse.json({ comicPanels }); - } catch (error) { - console.error("生成漫画脚本错误:", error); - return NextResponse.json( - { error: "生成漫画脚本时发生错误" }, - { status: 500 } - ); - } -} diff --git a/.history/app/api/generate/route_20250802003415.ts b/.history/app/api/generate/route_20250802003415.ts deleted file mode 100644 index 3a887a5..0000000 --- a/.history/app/api/generate/route_20250802003415.ts +++ /dev/null @@ -1,128 +0,0 @@ -import { NextResponse } from "next/server"; -import { ComicPanel } from "@/lib/types"; - -// 支持的模型列表 -const SUPPORTED_MODELS: Record = { - "qwen-32b": "Qwen/QwQ-32B", - "deepseek-7b": "deepseek-ai/deepseek-coder-7b-instruct", // 示例模型ID - "gpt-3.5": "gpt-3.5-turbo", // 示例模型ID -}; - -export async function POST(request: Request) { - try { - const { - content, - count, - sceneNumber, - model = "qwen-32b", - } = await request.json(); - - if (!content) { - return NextResponse.json({ error: "内容不能为空" }, { status: 400 }); - } - - // 验证模型是否支持 - const modelKey = model as string; - if (!SUPPORTED_MODELS[modelKey]) { - return NextResponse.json({ error: "不支持的模型" }, { status: 400 }); - } - - // 模型ID - const modelId = SUPPORTED_MODELS[modelKey]; - - // 构建提示词 - let prompt; - - // 如果指定了场景编号,则只生成该场景 - if (typeof sceneNumber === "number") { - prompt = `请你根据以下内容生成一个漫画场景的描述,该场景是第${sceneNumber}个场景,生成的漫画描述应该简洁、具体且富有视觉感。 - - 内容: ${content} - - 请直接给出第${sceneNumber}个场景的描述,不要有多余的解释。`; - } else { - // 否则生成多个场景 - prompt = `请你根据以下内容生成${count}个漫画场景的描述,每个场景的描述应该简洁、具体且富有视觉感。 - - 内容: ${content} - - 请直接给出${count}个场景的描述,每个场景单独一段,格式如下: - 场景1:[场景1的描述] - 场景2:[场景2的描述] - ... - - 不要有多余的解释。`; - } - - // 调用 DeepSeek API - const response = await fetch( - "https://api.deepseek.com/v1/chat/completions", - { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${process.env.DEEPSEEK_API_KEY}`, - }, - body: JSON.stringify({ - model: modelId, - messages: [ - { - role: "user", - content: prompt, - }, - ], - temperature: 0.7, - max_tokens: 1000, - }), - } - ); - - if (!response.ok) { - const errorData = await response.json(); - console.error("DeepSeek API错误:", errorData); - return NextResponse.json( - { error: "调用AI服务失败,请稍后再试" }, - { status: 500 } - ); - } - - const responseData = await response.json(); - const generatedText = responseData.choices[0].message.content; - - console.log("生成的内容:", generatedText); - - let comicPanels: ComicPanel[] = []; - - // 处理单个场景刷新的情况 - if (typeof sceneNumber === "number") { - comicPanels = [ - { - id: `scene-${sceneNumber}`, - content: generatedText.trim(), - imageUrl: `https://placehold.co/600x400?text=场景${sceneNumber}`, - sceneNumber: sceneNumber, - }, - ]; - } else { - // 处理多个场景的情况 - const scenes = generatedText - .split(/场景\d+[::]/) - .filter((scene: string) => scene.trim().length > 0); - - comicPanels = scenes.map((scene: string, index: number) => ({ - id: `scene-${index + 1}`, - content: scene.trim(), - imageUrl: `https://placehold.co/600x400?text=场景${index + 1}`, - sceneNumber: index + 1, - })); - } - - return NextResponse.json({ comicPanels }); - } catch (error) { - console.error("生成漫画脚本错误:", error); - return NextResponse.json( - { error: "生成漫画脚本时发生错误" }, - { status: 500 } - ); - } -} diff --git a/.history/app/api/generate/route_20250802004958.ts b/.history/app/api/generate/route_20250802004958.ts deleted file mode 100644 index 3a887a5..0000000 --- a/.history/app/api/generate/route_20250802004958.ts +++ /dev/null @@ -1,128 +0,0 @@ -import { NextResponse } from "next/server"; -import { ComicPanel } from "@/lib/types"; - -// 支持的模型列表 -const SUPPORTED_MODELS: Record = { - "qwen-32b": "Qwen/QwQ-32B", - "deepseek-7b": "deepseek-ai/deepseek-coder-7b-instruct", // 示例模型ID - "gpt-3.5": "gpt-3.5-turbo", // 示例模型ID -}; - -export async function POST(request: Request) { - try { - const { - content, - count, - sceneNumber, - model = "qwen-32b", - } = await request.json(); - - if (!content) { - return NextResponse.json({ error: "内容不能为空" }, { status: 400 }); - } - - // 验证模型是否支持 - const modelKey = model as string; - if (!SUPPORTED_MODELS[modelKey]) { - return NextResponse.json({ error: "不支持的模型" }, { status: 400 }); - } - - // 模型ID - const modelId = SUPPORTED_MODELS[modelKey]; - - // 构建提示词 - let prompt; - - // 如果指定了场景编号,则只生成该场景 - if (typeof sceneNumber === "number") { - prompt = `请你根据以下内容生成一个漫画场景的描述,该场景是第${sceneNumber}个场景,生成的漫画描述应该简洁、具体且富有视觉感。 - - 内容: ${content} - - 请直接给出第${sceneNumber}个场景的描述,不要有多余的解释。`; - } else { - // 否则生成多个场景 - prompt = `请你根据以下内容生成${count}个漫画场景的描述,每个场景的描述应该简洁、具体且富有视觉感。 - - 内容: ${content} - - 请直接给出${count}个场景的描述,每个场景单独一段,格式如下: - 场景1:[场景1的描述] - 场景2:[场景2的描述] - ... - - 不要有多余的解释。`; - } - - // 调用 DeepSeek API - const response = await fetch( - "https://api.deepseek.com/v1/chat/completions", - { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${process.env.DEEPSEEK_API_KEY}`, - }, - body: JSON.stringify({ - model: modelId, - messages: [ - { - role: "user", - content: prompt, - }, - ], - temperature: 0.7, - max_tokens: 1000, - }), - } - ); - - if (!response.ok) { - const errorData = await response.json(); - console.error("DeepSeek API错误:", errorData); - return NextResponse.json( - { error: "调用AI服务失败,请稍后再试" }, - { status: 500 } - ); - } - - const responseData = await response.json(); - const generatedText = responseData.choices[0].message.content; - - console.log("生成的内容:", generatedText); - - let comicPanels: ComicPanel[] = []; - - // 处理单个场景刷新的情况 - if (typeof sceneNumber === "number") { - comicPanels = [ - { - id: `scene-${sceneNumber}`, - content: generatedText.trim(), - imageUrl: `https://placehold.co/600x400?text=场景${sceneNumber}`, - sceneNumber: sceneNumber, - }, - ]; - } else { - // 处理多个场景的情况 - const scenes = generatedText - .split(/场景\d+[::]/) - .filter((scene: string) => scene.trim().length > 0); - - comicPanels = scenes.map((scene: string, index: number) => ({ - id: `scene-${index + 1}`, - content: scene.trim(), - imageUrl: `https://placehold.co/600x400?text=场景${index + 1}`, - sceneNumber: index + 1, - })); - } - - return NextResponse.json({ comicPanels }); - } catch (error) { - console.error("生成漫画脚本错误:", error); - return NextResponse.json( - { error: "生成漫画脚本时发生错误" }, - { status: 500 } - ); - } -} diff --git a/.history/app/api/generate/route_20250802204037.ts b/.history/app/api/generate/route_20250802204037.ts deleted file mode 100644 index 99a1af0..0000000 --- a/.history/app/api/generate/route_20250802204037.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { NextResponse } from "next/server"; -import { ComicPanel } from "@/lib/types"; - -// DeepSeek API配置 -const DEEPSEEK_API_URL = "https://api.deepseek.com/v1/chat/completions"; -const DEEPSEEK_API_KEY = "sk-xgpdmqrgikwkbaacvxbvtkwhxnwjncdvqzlshnzcyswxhsfn"; // 用户提供的API密钥 - -// 支持的模型列表 -const SUPPORTED_MODELS: Record = { - "qwen-32b": "Qwen/QwQ-32B", - "deepseek-7b": "deepseek-ai/deepseek-coder-7b-instruct", // 示例模型ID - "gpt-3.5": "gpt-3.5-turbo", // 示例模型ID -}; - -export async function POST(request: Request) { - try { - const { - content, - count, - sceneNumber, - model = "qwen-32b", - } = await request.json(); - - if (!content) { - return NextResponse.json({ error: "内容不能为空" }, { status: 400 }); - } - - // 验证模型是否支持 - const modelKey = model as string; - if (!SUPPORTED_MODELS[modelKey]) { - return NextResponse.json({ error: "不支持的模型" }, { status: 400 }); - } - - // 模型ID - const modelId = SUPPORTED_MODELS[modelKey]; - - // 构建提示词 - let prompt; - - // 如果指定了场景编号,则只生成该场景 - if (typeof sceneNumber === "number") { - prompt = `请你根据以下内容生成一个漫画场景的描述,该场景是第${sceneNumber}个场景,生成的漫画描述应该简洁、具体且富有视觉感。 - - 内容: ${content} - - 请直接给出第${sceneNumber}个场景的描述,不要有多余的解释。`; - } else { - // 否则生成多个场景 - prompt = `请你根据以下内容生成${count}个漫画场景的描述,每个场景的描述应该简洁、具体且富有视觉感。 - - 内容: ${content} - - 请直接给出${count}个场景的描述,每个场景单独一段,格式如下: - 场景1:[场景1的描述] - 场景2:[场景2的描述] - ... - - 不要有多余的解释。`; - } - - // 调用 DeepSeek API - const response = await fetch(DEEPSEEK_API_URL, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${DEEPSEEK_API_KEY}`, - }, - body: JSON.stringify({ - model: modelId, - messages: [ - { - role: "user", - content: prompt, - }, - ], - temperature: 0.7, - max_tokens: 1000, - }), - }); - - // 检查API响应状态 - if (!response.ok) { - const errorData = await response.json(); - console.error("DeepSeek API错误:", errorData); - - // 提供更具体的错误信息 - let errorMessage = "调用AI服务失败,请稍后再试"; - if (errorData.error && errorData.error.message) { - if (errorData.error.type === "authentication_error") { - errorMessage = "API密钥验证失败,请联系管理员更新API密钥"; - } else { - errorMessage = `AI服务错误: ${errorData.error.message}`; - } - } - - return NextResponse.json({ error: errorMessage }, { status: 500 }); - } - - const responseData = await response.json(); - const generatedText = responseData.choices[0].message.content; - - console.log("生成的内容:", generatedText); - - let comicPanels: ComicPanel[] = []; - - // 处理单个场景刷新的情况 - if (typeof sceneNumber === "number") { - comicPanels = [ - { - id: `scene-${sceneNumber}`, - content: generatedText.trim(), - imageUrl: `https://placehold.co/600x400?text=场景${sceneNumber}`, - sceneNumber: sceneNumber, - }, - ]; - } else { - // 处理多个场景的情况 - const scenes = generatedText - .split(/场景\d+[::]/) - .filter((scene: string) => scene.trim().length > 0); - - comicPanels = scenes.map((scene: string, index: number) => ({ - id: `scene-${index + 1}`, - content: scene.trim(), - imageUrl: `https://placehold.co/600x400?text=场景${index + 1}`, - sceneNumber: index + 1, - })); - } - - return NextResponse.json({ comicPanels }); - } catch (error) { - console.error("生成漫画脚本错误:", error); - return NextResponse.json( - { error: "生成漫画脚本时发生错误" }, - { status: 500 } - ); - } -} diff --git a/.history/app/api/generate/route_20250802204640.ts b/.history/app/api/generate/route_20250802204640.ts deleted file mode 100644 index 2973f96..0000000 --- a/.history/app/api/generate/route_20250802204640.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { NextResponse } from "next/server"; -import { ComicPanel } from "@/lib/types"; - -// DeepSeek API配置 -const DEEPSEEK_API_URL = "https://api.siliconflow.cn/v1/chat/completions"; // 修正为正确的API端点 -const DEEPSEEK_API_KEY = "sk-xgpdmqrgikwkbaacvxbvtkwhxnwjncdvqzlshnzcyswxhsfn"; // 用户提供的API密钥 - -// 支持的模型列表 -const SUPPORTED_MODELS: Record = { - "qwen-32b": "Qwen/QwQ-32B", - "deepseek-7b": "deepseek-ai/deepseek-coder-7b-instruct", // 示例模型ID - "gpt-3.5": "gpt-3.5-turbo", // 示例模型ID -}; - -export async function POST(request: Request) { - try { - const { - content, - count, - sceneNumber, - model = "qwen-32b", - } = await request.json(); - - if (!content) { - return NextResponse.json({ error: "内容不能为空" }, { status: 400 }); - } - - // 验证模型是否支持 - const modelKey = model as string; - if (!SUPPORTED_MODELS[modelKey]) { - return NextResponse.json({ error: "不支持的模型" }, { status: 400 }); - } - - // 模型ID - const modelId = SUPPORTED_MODELS[modelKey]; - - // 构建提示词 - let prompt; - - // 如果指定了场景编号,则只生成该场景 - if (typeof sceneNumber === "number") { - prompt = `请你根据以下内容生成一个漫画场景的描述,该场景是第${sceneNumber}个场景,生成的漫画描述应该简洁、具体且富有视觉感。 - - 内容: ${content} - - 请直接给出第${sceneNumber}个场景的描述,不要有多余的解释。`; - } else { - // 否则生成多个场景 - prompt = `请你根据以下内容生成${count}个漫画场景的描述,每个场景的描述应该简洁、具体且富有视觉感。 - - 内容: ${content} - - 请直接给出${count}个场景的描述,每个场景单独一段,格式如下: - 场景1:[场景1的描述] - 场景2:[场景2的描述] - ... - - 不要有多余的解释。`; - } - - // 调用 DeepSeek API - const response = await fetch(DEEPSEEK_API_URL, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${DEEPSEEK_API_KEY}`, - }, - body: JSON.stringify({ - model: modelId, - messages: [ - { - role: "user", - content: prompt, - }, - ], - temperature: 0.7, - max_tokens: 1000, - }), - }); - - // 检查API响应状态 - if (!response.ok) { - const errorData = await response.json(); - console.error("DeepSeek API错误:", errorData); - - // 提供更具体的错误信息 - let errorMessage = "调用AI服务失败,请稍后再试"; - if (errorData.error && errorData.error.message) { - if (errorData.error.type === "authentication_error") { - errorMessage = "API密钥验证失败,请联系管理员更新API密钥"; - } else { - errorMessage = `AI服务错误: ${errorData.error.message}`; - } - } - - return NextResponse.json({ error: errorMessage }, { status: 500 }); - } - - const responseData = await response.json(); - const generatedText = responseData.choices[0].message.content; - - console.log("生成的内容:", generatedText); - - let comicPanels: ComicPanel[] = []; - - // 处理单个场景刷新的情况 - if (typeof sceneNumber === "number") { - comicPanels = [ - { - id: `scene-${sceneNumber}`, - content: generatedText.trim(), - imageUrl: `https://placehold.co/600x400?text=场景${sceneNumber}`, - sceneNumber: sceneNumber, - }, - ]; - } else { - // 处理多个场景的情况 - const scenes = generatedText - .split(/场景\d+[::]/) - .filter((scene: string) => scene.trim().length > 0); - - comicPanels = scenes.map((scene: string, index: number) => ({ - id: `scene-${index + 1}`, - content: scene.trim(), - imageUrl: `https://placehold.co/600x400?text=场景${index + 1}`, - sceneNumber: index + 1, - })); - } - - return NextResponse.json({ comicPanels }); - } catch (error) { - console.error("生成漫画脚本错误:", error); - return NextResponse.json( - { error: "生成漫画脚本时发生错误" }, - { status: 500 } - ); - } -} diff --git a/.history/app/api/generate/route_20250803171851.ts b/.history/app/api/generate/route_20250803171851.ts deleted file mode 100644 index 004e540..0000000 --- a/.history/app/api/generate/route_20250803171851.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { NextResponse } from "next/server"; -import { ComicPanel } from "@/lib/types"; - -// DeepSeek API配置 -const DEEPSEEK_API_URL = "https://api.siliconflow.cn/v1/chat/completions"; -// 优先使用环境变量中的API密钥,如果不存在则使用硬编码的密钥 -const DEEPSEEK_API_KEY = - process.env.DEEPSEEK_API_KEY || - "sk-xgpdmqrgikwkbaacvxbvtkwhxnwjncdvqzlshnzcyswxhsfn"; - -// 支持的模型列表 -const SUPPORTED_MODELS: Record = { - "qwen-32b": "Qwen/QwQ-32B", - "deepseek-7b": "deepseek-ai/deepseek-coder-7b-instruct", // 示例模型ID - "gpt-3.5": "gpt-3.5-turbo", // 示例模型ID -}; - -export async function POST(request: Request) { - try { - const { - content, - count, - sceneNumber, - model = "qwen-32b", - } = await request.json(); - - if (!content) { - return NextResponse.json({ error: "内容不能为空" }, { status: 400 }); - } - - // 验证模型是否支持 - const modelKey = model as string; - if (!SUPPORTED_MODELS[modelKey]) { - return NextResponse.json({ error: "不支持的模型" }, { status: 400 }); - } - - // 模型ID - const modelId = SUPPORTED_MODELS[modelKey]; - - // 构建提示词 - let prompt; - - // 如果指定了场景编号,则只生成该场景 - if (typeof sceneNumber === "number") { - prompt = `请你根据以下内容生成一个漫画场景的描述,该场景是第${sceneNumber}个场景,生成的漫画描述应该简洁、具体且富有视觉感。 - - 内容: ${content} - - 请直接给出第${sceneNumber}个场景的描述,不要有多余的解释。`; - } else { - // 否则生成多个场景 - prompt = `请你根据以下内容生成${count}个漫画场景的描述,每个场景的描述应该简洁、具体且富有视觉感。 - - 内容: ${content} - - 请直接给出${count}个场景的描述,每个场景单独一段,格式如下: - 场景1:[场景1的描述] - 场景2:[场景2的描述] - ... - - 不要有多余的解释。`; - } - - // 调用 DeepSeek API - const response = await fetch(DEEPSEEK_API_URL, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${DEEPSEEK_API_KEY}`, - }, - body: JSON.stringify({ - model: modelId, - messages: [ - { - role: "user", - content: prompt, - }, - ], - temperature: 0.7, - max_tokens: 1000, - }), - }); - - // 检查API响应状态 - if (!response.ok) { - const errorData = await response.json(); - console.error("DeepSeek API错误:", errorData); - - // 提供更具体的错误信息 - let errorMessage = "调用AI服务失败,请稍后再试"; - if (errorData.error && errorData.error.message) { - if (errorData.error.type === "authentication_error") { - errorMessage = "API密钥验证失败,请联系管理员更新API密钥"; - } else { - errorMessage = `AI服务错误: ${errorData.error.message}`; - } - } - - return NextResponse.json({ error: errorMessage }, { status: 500 }); - } - - const responseData = await response.json(); - const generatedText = responseData.choices[0].message.content; - - console.log("生成的内容:", generatedText); - - let comicPanels: ComicPanel[] = []; - - // 处理单个场景刷新的情况 - if (typeof sceneNumber === "number") { - comicPanels = [ - { - id: `scene-${sceneNumber}`, - content: generatedText.trim(), - imageUrl: `https://placehold.co/600x400?text=场景${sceneNumber}`, - sceneNumber: sceneNumber, - }, - ]; - } else { - // 处理多个场景的情况 - const scenes = generatedText - .split(/场景\d+[::]/) - .filter((scene: string) => scene.trim().length > 0); - - comicPanels = scenes.map((scene: string, index: number) => ({ - id: `scene-${index + 1}`, - content: scene.trim(), - imageUrl: `https://placehold.co/600x400?text=场景${index + 1}`, - sceneNumber: index + 1, - })); - } - - return NextResponse.json({ comicPanels }); - } catch (error) { - console.error("生成漫画脚本错误:", error); - return NextResponse.json( - { error: "生成漫画脚本时发生错误" }, - { status: 500 } - ); - } -} diff --git a/.history/app/page_20250727100327.tsx b/.history/app/page_20250727100327.tsx deleted file mode 100644 index 6a5d6bf..0000000 --- a/.history/app/page_20250727100327.tsx +++ /dev/null @@ -1,243 +0,0 @@ -"use client" - -import { useState } from "react" -import { Card, CardContent } from "@/components/ui/card" -import { Button } from "@/components/ui/button" -import { Textarea } from "@/components/ui/textarea" -import { Badge } from "@/components/ui/badge" -import { Sparkles, ImageIcon, Download, Eye, Heart, MessageCircle, Share } from "lucide-react" - -export default function ComicGenerator() { - const [content, setContent] = useState("") - const [selectedCount, setSelectedCount] = useState(4) - const [isGenerating, setIsGenerating] = useState(false) - const [showResults, setShowResults] = useState(false) - const maxLength = 2000 - - const sampleArticle = `春天来了,小明走在回家的路上。突然,他发现路边有一只受伤的小猫。小明毫不犹豫地将小猫抱起,送到了附近的宠物医院。医生说小猫只是轻微擦伤,很快就能康复。从那天起,小明每天都会去医院看望小猫,直到它完全康复。` - - const handleGenerate = async () => { - if (!content.trim()) return - - setIsGenerating(true) - // 模拟生成过程 - setTimeout(() => { - setIsGenerating(false) - setShowResults(true) - }, 3000) - } - - const fillSample = () => { - setContent(sampleArticle) - } - - const getGridLayout = (count: number) => { - switch (count) { - case 2: - return "grid-cols-1 md:grid-cols-2" - case 4: - return "grid-cols-2" - case 6: - return "grid-cols-2 md:grid-cols-3" - case 8: - return "grid-cols-2 md:grid-cols-4" - default: - return "grid-cols-2" - } - } - - return ( -
- {/* Header */} -
-
-
-
- -
- 漫画生成器 -
-
- - Beta - -
-
-
- -
- {/* Hero Section */} -
-

- 文章转漫画生成器 -

-

将你的文字变成精彩的漫画故事 ✨

-
- - {/* Input Section */} - - -
-
- - maxLength ? "text-red-500" : "text-gray-500"}`}> - 还可以输入 {maxLength - content.length} 字 - -
- -