const models = require('../../models'); const fetch = require('node-fetch'); let apiKey = require('process').env.ARK_API_KEY; const url = "https://ark.cn-beijing.volces.com/api/v3/chat/completions"; let headers = { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' } /** * 豆包视觉API * 从豆包获取图片的标题和文案描述 * @param {*} imageUrl */ async function fetchMetaByImageFromDoubao(imageUrl) { let data = { "model": "ep-20250206115552-7qg5c", // Doubao-1.5...ion-pro-32k 当前最新,贵,响应慢,效果好 // "model": "ep-20250204231910-4phb8", // Doubao-vision-lite-32k 便宜点,相应速度快 "messages": [ { "role": "user", "content": [ { "type": "text", "text": "这是一张填色页图片,根据图片生成简短标题(title,无需包含填色页关键字)、SEO标题(seotitle, 用于网站SEO 60个字符以内,包含填色页关键字)、简要描述(description, 用于网站SEO的meta description,160个字符以内,包含填色页关键字)、文案描述(copy, 200字左右)。以json格式输出,支持语言中文(zh)、英语(en)、西班牙语(es)、葡萄牙语(pt)、日语(ja),形如: { title: {zh:'', en:'', es: '', pt: '', ja: ''}, description: {zh:'', en:'', es: '', pt: '', ja: ''}, copy:{zh:'', en:'', es: '', pt: '', ja: ''} }" }, { "type": "image_url", "image_url": { "url": `${imageUrl}` } } ] } ] } console.log(data); const jsonData = JSON.stringify(data); const response = await fetch(url, { method: 'POST', headers, body: jsonData }); let responseJson = await response.json(); console.log(responseJson); return responseJson.choices[0].message.content; } /** * 豆包对话API * @param {*} text */ async function fetchMetaByTxtFromDoubao(text) { let data = { "model": "ep-20250206115552-7qg5c", // Doubao-1.5...ion-pro-32k 当前最新,贵,响应慢,效果好 // "model": "ep-20250204231910-4phb8", // Doubao-vision-lite-32k 便宜点,相应速度快 "messages": [ { "role": "user", "content": text } ] } console.log(data); const jsonData = JSON.stringify(data); const response = await fetch(url, { method: 'POST', headers, body: jsonData }); let responseJson = await response.json(); console.log(responseJson); return responseJson.choices[0].message.content; } async function runAlbumMeta() { let done = 0; let duration = 0; let hour, minute, second; let start = Date.now(); // 筛选出所有album let query = { pid: 'art', enabled: true, $or: [{ seoTitle: { $exists: false } }, { seoTitle: null }] } let docs = await models.ArtAlbum .find(query) .sort({ order: 'asc' }) .populate('title') .select('tag title slogon'); let total = docs.length; console.log('total:', total); if (total <= 0) return; for (let doc of docs) { console.log(`process album ${doc._id}`); console.time(doc._id); try { let text = `帮我生成填色页专辑《${doc.title.zh}》的SEO title(60个字符以内) 和 description(160个字符以内), 以json格式输出,支持语言中文(zh)、英语(en)、西班牙语(es)、葡萄牙语(pt)、日语(ja),形如: { title: {zh:'', en:'', es: '', pt: '', ja: ''}, description: {zh:'', en:'', es: '', pt: '', ja: ''}`; let metaInfo = await fetchMetaByTxtFromDoubao(text); console.log(metaInfo); let metaInfoJson = JSON.parse(metaInfo); let titleJson = metaInfoJson.title; let descJson = metaInfoJson.description; let title = JSON.stringify(titleJson); let desc = JSON.stringify(descJson); doc.seoTitle = title; doc.seoDescription = desc; await doc.save(); } catch (e) { console.error(e.message); } console.timeEnd(doc._id); done++; duration = (Date.now() - start) / 1000; hour = (Math.floor(duration / 60 / 60)).toString().padStart(2, '0'); minute = (Math.floor(duration / 60) % 60).toString().padStart(2, '0'); second = (Math.floor(duration) % 60).toString().padStart(2, '0'); console.log('progress: ' + Math.floor((100 * done / total)) + '% used time: ' + hour + ':' + minute + ':' + second); } } async function runVideoStoryMeta() { let done = 0; let duration = 0; let hour, minute, second; let start = Date.now(); // 筛选出所有videostory let query = { enabled: true, $or: [{ seoTitle: { $exists: false } }, { seoTitle: null }] } let docs = await models.ArtVideoStory .find(query) .sort({ order: 'asc' }) .select('name'); let total = docs.length; console.log('total:', total); if (total <= 0) return; for (let doc of docs) { console.log(`process video-story ${doc._id}`); console.time(doc._id); try { let text = `帮我生成填色页视频故事《${doc.name}》的SEO title(60个字符以内) 和 description(160个字符以内), 以json格式输出,支持语言中文(zh)、英语(en)、西班牙语(es)、葡萄牙语(pt)、日语(ja),形如: { title: {zh:'', en:'', es: '', pt: '', ja: ''}, description: {zh:'', en:'', es: '', pt: '', ja: ''}`; let metaInfo = await fetchMetaByTxtFromDoubao(text); console.log(metaInfo); let metaInfoJson = JSON.parse(metaInfo); let titleJson = metaInfoJson.title; let descJson = metaInfoJson.description; let title = JSON.stringify(titleJson); let desc = JSON.stringify(descJson); doc.seoTitle = title; doc.seoDescription = desc; await doc.save(); } catch (e) { console.error(e.message); } console.timeEnd(doc._id); done++; duration = (Date.now() - start) / 1000; hour = (Math.floor(duration / 60 / 60)).toString().padStart(2, '0'); minute = (Math.floor(duration / 60) % 60).toString().padStart(2, '0'); second = (Math.floor(duration) % 60).toString().padStart(2, '0'); console.log('progress: ' + Math.floor((100 * done / total)) + '% used time: ' + hour + ':' + minute + ':' + second); } } async function runArtMeta() { let done = 0; let duration = 0; let hour, minute, second; let start = Date.now(); // 筛选出所有已经ready并且还没有title的图 let query = { status: { $gte: 7000 }, $or: [{ seoTitle: { $exists: false } }, { seoTitle: null }] }; let docs = await models.Art.find(query).limit(1000).sort({ publishTime: 'desc' }); // 内存有限,每次跑1000个 let total = docs.length; console.log('total:', total); if (total <= 0) return; for (let doc of docs) { console.log(`process ${doc._id}`); let thumbUrl = `http://color.jccytech.cn/thumbs/v2/work/320/${doc._id}.png`; if (doc.hasSpecial) { thumbUrl = `http://color.jccytech.cn/thumbs/v2/special/320/${doc._id}.png`; } console.time(doc._id); try { let metaInfo = await fetchMetaByImageFromDoubao(thumbUrl); console.log(metaInfo); let metaInfoJson = JSON.parse(metaInfo); let titleJson = metaInfoJson.title; let seotitleJson = metaInfoJson.seotitle; let descJson = metaInfoJson.description; let copyJson = metaInfoJson.copy; let title = JSON.stringify(titleJson); let seotitle = JSON.stringify(seotitleJson); let desc = JSON.stringify(descJson); let copy = JSON.stringify(copyJson); doc.seoTitle = seotitle; doc.seoDescription = desc; doc.title = title; doc.desc = copy; await doc.save(); } catch (e) { console.error(e.message); } console.timeEnd(doc._id); done++; duration = (Date.now() - start) / 1000; hour = (Math.floor(duration / 60 / 60)).toString().padStart(2, '0'); minute = (Math.floor(duration / 60) % 60).toString().padStart(2, '0'); second = (Math.floor(duration) % 60).toString().padStart(2, '0'); console.log('progress: ' + Math.floor((100 * done / total)) + '% used time: ' + hour + ':' + minute + ':' + second); } runArtMeta(); } async function run() { await runAlbumMeta(); await runVideoStoryMeta(); await runArtMeta(); } async function test() { let metaInfo = await fetchMetaByImageFromDoubao("https://color.jccytech.cn/thumbs/v2/work/640/67a254ec4f9d65537938e5c5.png"); console.log(metaInfo); let metaInfoJson = JSON.parse(metaInfo); let titleJson = metaInfoJson.title; let descJson = metaInfoJson.copy; let title = JSON.stringify(titleJson); let desc = JSON.stringify(descJson); console.log(title); console.log(desc); } module.exports = { run } if (require.main == module) { run(); }