| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 |
- 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);
- if (responseJson.choices) return responseJson.choices[0].message.content;
- else return null;
- }
- /**
- * 豆包对话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);
- if (!metaInfo) {
- console.error("something wrong, quit!");
- process.exit();
- }
- 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();
- }
|