/** * 云函数:quicklyVideo(一键成片代理) * 替代: * POST /api/quickly/create → action=create * POST /api/quickly/query → action=query * * 把 server.js 里的签名 + relay 逻辑搬到云函数, * 隐藏 APP_KEY / APP_SECRET,前端只需传 videoUrls。 * * ⚠️ 不是参考别项目,是本项目 server.js 1820~1914 行的等价实现。 */ const crypto = require('crypto'); const QUICKLY_APP_KEY = 'ZmNmOGRhNjYzZTAx'; const QUICKLY_APP_SECRET = 'eaa12154c248cad9159a9d6ea8bedf46'; const QUICKLY_ACCOUNT_ID = '12859_117409'; const QUICKLY_CALLBACK_URL = 'https://server.fmode.cn/api/functions/cut/onemerge'; const QUICKLY_RELAY_URL = 'https://server.fmode.cn/api/functions'; // 上游筷子代理云函数 ID(fmode 平台已有) const UPSTREAM_FN_ID = 'sWvRr8RvPT'; async function handler(request, response) { try { const action = pickParam(request, 'action'); if (action === 'create') { const videoUrls = pickParam(request, 'videoUrls'); const options = pickParam(request, 'options') || {}; if (!Array.isArray(videoUrls) || videoUrls.length === 0) { return response.json({ code: 400, success: false, error: '缺少 videoUrls 参数' }); } const timestamp = Date.now().toString(); const sign = crypto.createHash('md5').update(timestamp + '#' + QUICKLY_APP_SECRET).digest('hex'); const apiBody = { account_id: QUICKLY_ACCOUNT_ID, callback_url: QUICKLY_CALLBACK_URL, material_list: videoUrls.map(url => ({ type: 'video', value: url })), tags: options.tags || '视频,AI生成', proportion: options.proportion || '9:16', video_duration: options.videoDuration || { min: 10, max: 30 }, pre_id: timestamp, compose_number: 1, ai_voice: options.aiVoice ?? 1, ai_bgm: options.aiBgm ?? 1, ai_flower: 1, ai_subtitle: options.aiSubtitle ?? 0, original_voice: 0 }; const relayBody = { action: 'relay', relayData: JSON.stringify({ apiPath: '/v2/video/vlog/create', apiBody, appKey: QUICKLY_APP_KEY, timestamp, sign }) }; console.log(`📦 quicklyVideo.create timestamp=${timestamp} videos=${videoUrls.length}`); const r = await fetch(QUICKLY_RELAY_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(relayBody) }); const result = await r.json(); response.json({ code: 200, success: true, data: result }); return; } if (action === 'query') { const taskId = pickParam(request, 'taskId'); if (!taskId) return response.json({ code: 400, success: false, error: '缺少 taskId' }); const r = await fetch(QUICKLY_RELAY_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: UPSTREAM_FN_ID, _ApplicationId: 'ncloudmaster', action: 'query', taskId }) }); const result = await r.json(); response.json({ code: 200, success: true, data: result }); return; } response.json({ code: 400, success: false, error: `未知 action: ${action}` }); } catch (error) { console.error('❌ quicklyVideo 失败:', error.message); response.json({ code: 500, success: false, error: error.message }); } } function pickParam(request, ...names) { const sources = [request.params, request.body, request]; for (const src of sources) { if (!src || typeof src !== 'object') continue; for (const n of names) { const v = src[n]; if (v !== undefined && v !== null && v !== '') return v; } } return null; }