Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 | /** * 视频生成模块 - API 路由控制器 */ import Router from '@koa/router'; import { createVideoProject, getVideoProjects, getVideoProject, updateVideoProject, deleteVideoProject, generateVideoForProject, getGenerateProgress, getMaterials, uploadMaterial, deleteMaterial, createVideoProjectFromBook, } from './video-generator.service'; const router = new Router(); // ============ 视频项目管理 ============ /** * GET /api/video/projects * 获取视频项目列表 */ router.get('/projects', async (ctx) => { const query = { userId: ctx.query.userId ? Number(ctx.query.userId) : undefined, status: ctx.query.status as any, page: ctx.query.page ? Number(ctx.query.page) : 1, pageSize: ctx.query.pageSize ? Number(ctx.query.pageSize) : 10, }; const result = await getVideoProjects(query); ctx.body = { success: true, data: result }; }); /** * POST /api/video/projects * 创建视频项目 */ router.post('/projects', async (ctx) => { const body = ctx.request.body as any; const userId = ctx.state.user?.id; const project = await createVideoProject(body, userId); ctx.body = { success: true, data: project }; }); /** * GET /api/video/projects/:id * 获取视频项目详情 */ router.get('/projects/:id', async (ctx) => { const id = Number(ctx.params.id); const project = await getVideoProject(id); if (!project) { ctx.status = 404; ctx.body = { success: false, error: '项目不存在' }; return; } ctx.body = { success: true, data: project }; }); /** * PUT /api/video/projects/:id * 更新视频项目 */ router.put('/projects/:id', async (ctx) => { const id = Number(ctx.params.id); const body = ctx.request.body as any; const project = await updateVideoProject(id, body); if (!project) { ctx.status = 404; ctx.body = { success: false, error: '项目不存在' }; return; } ctx.body = { success: true, data: project }; }); /** * DELETE /api/video/projects/:id * 删除视频项目 */ router.delete('/projects/:id', async (ctx) => { const id = Number(ctx.params.id); const success = await deleteVideoProject(id); if (!success) { ctx.status = 404; ctx.body = { success: false, error: '项目不存在' }; return; } ctx.body = { success: true }; }); // ============ 视频生成 ============ /** * POST /api/video/projects/:id/generate * 开始生成视频 */ router.post('/projects/:id/generate', async (ctx) => { const id = Number(ctx.params.id); const result = await generateVideoForProject(id); if (!result.success) { ctx.status = 400; ctx.body = { success: false, error: result.error }; return; } ctx.body = { success: true, data: { outputUrl: result.outputUrl, duration: result.duration, fileSize: result.fileSize, }, }; }); /** * GET /api/video/projects/:id/status * 获取生成状态 */ router.get('/projects/:id/status', async (ctx) => { const id = Number(ctx.params.id); const status = await getGenerateProgress(id); ctx.body = { success: true, data: status }; }); // ============ 素材管理 ============ /** * GET /api/video/materials * 获取素材列表 */ router.get('/materials', async (ctx) => { const query = { userId: ctx.query.userId ? Number(ctx.query.userId) : undefined, type: ctx.query.type as any, category: ctx.query.category as string, page: ctx.query.page ? Number(ctx.query.page) : 1, pageSize: ctx.query.pageSize ? Number(ctx.query.pageSize) : 20, }; const result = await getMaterials(query); ctx.body = { success: true, data: result }; }); /** * POST /api/video/materials/upload * 上传素材(处理 multipart/form-data 文件上传) */ router.post('/materials/upload', async (ctx) => { const userId = ctx.state.user?.id; // 处理 multipart form data const body = ctx.request.body as any; const files = (ctx.request as any).files as any; // 获取上传的文件 const file = files?.file; let materialUrl = ''; if (file) { // 文件已上传,url 就是文件的路径 materialUrl = `/uploads/materials/${file.newFilename || file.filename}`; } else if (body.url) { // 如果没有文件,使用传入的url materialUrl = body.url; } else { ctx.status = 400; ctx.body = { success: false, error: 'No file uploaded' }; return; } const material = await uploadMaterial({ type: body.type || 'image', name: body.name || file?.newFilename || 'unnamed', url: materialUrl, thumbnail: body.thumbnail, tags: body.tags ? JSON.parse(body.tags) : [], category: body.category, duration: body.duration ? Number(body.duration) : undefined, size: body.size ? Number(body.size) : undefined, width: body.width ? Number(body.width) : undefined, height: body.height ? Number(body.height) : undefined, }, userId); ctx.body = { success: true, data: material }; }); /** * DELETE /api/video/materials/:id * 删除素材 */ router.delete('/materials/:id', async (ctx) => { const id = Number(ctx.params.id); const success = await deleteMaterial(id); if (!success) { ctx.status = 404; ctx.body = { success: false, error: '素材不存在' }; return; } ctx.body = { success: true }; }); // ============ 快捷入口 ============ /** * POST /api/video/books/:bookId/generate * 从书籍生成视频项目 */ router.post('/books/:bookId/generate', async (ctx) => { const bookId = Number(ctx.params.bookId); const userId = ctx.state.user?.id; const project = await createVideoProjectFromBook(bookId, userId); if (!project) { ctx.status = 404; ctx.body = { success: false, error: '书籍不存在' }; return; } ctx.body = { success: true, data: project }; }); export default router; |