fetch-meta.js 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. const models = require('../../models');
  2. const fetch = require('node-fetch');
  3. let apiKey = require('process').env.ARK_API_KEY;
  4. const url = "https://ark.cn-beijing.volces.com/api/v3/chat/completions";
  5. let headers = {
  6. 'Authorization': `Bearer ${apiKey}`,
  7. 'Content-Type': 'application/json'
  8. }
  9. /**
  10. * 豆包视觉API
  11. * 从豆包获取图片的标题和文案描述
  12. * @param {*} imageUrl
  13. */
  14. async function fetchMetaByImageFromDoubao(imageUrl) {
  15. let data = {
  16. "model": "ep-20250206115552-7qg5c", // Doubao-1.5...ion-pro-32k 当前最新,贵,响应慢,效果好
  17. // "model": "ep-20250204231910-4phb8", // Doubao-vision-lite-32k 便宜点,相应速度快
  18. "messages": [
  19. {
  20. "role": "user", "content": [
  21. {
  22. "type": "text",
  23. "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: ''} }"
  24. },
  25. {
  26. "type": "image_url",
  27. "image_url": {
  28. "url": `${imageUrl}`
  29. }
  30. }
  31. ]
  32. }
  33. ]
  34. }
  35. console.log(data);
  36. const jsonData = JSON.stringify(data);
  37. const response = await fetch(url, { method: 'POST', headers, body: jsonData });
  38. let responseJson = await response.json();
  39. console.log(responseJson);
  40. if (responseJson.choices) return responseJson.choices[0].message.content;
  41. else return null;
  42. }
  43. /**
  44. * 豆包对话API
  45. * @param {*} text
  46. */
  47. async function fetchMetaByTxtFromDoubao(text) {
  48. let data = {
  49. "model": "ep-20250206115552-7qg5c", // Doubao-1.5...ion-pro-32k 当前最新,贵,响应慢,效果好
  50. // "model": "ep-20250204231910-4phb8", // Doubao-vision-lite-32k 便宜点,相应速度快
  51. "messages": [
  52. { "role": "user", "content": text }
  53. ]
  54. }
  55. console.log(data);
  56. const jsonData = JSON.stringify(data);
  57. const response = await fetch(url, { method: 'POST', headers, body: jsonData });
  58. let responseJson = await response.json();
  59. console.log(responseJson);
  60. return responseJson.choices[0].message.content;
  61. }
  62. async function runAlbumMeta() {
  63. let done = 0;
  64. let duration = 0;
  65. let hour, minute, second;
  66. let start = Date.now();
  67. // 筛选出所有album
  68. let query = { pid: 'art', enabled: true, $or: [{ seoTitle: { $exists: false } }, { seoTitle: null }] }
  69. let docs = await models.ArtAlbum
  70. .find(query)
  71. .sort({ order: 'asc' })
  72. .populate('title')
  73. .select('tag title slogon');
  74. let total = docs.length;
  75. console.log('total:', total);
  76. if (total <= 0) return;
  77. for (let doc of docs) {
  78. console.log(`process album ${doc._id}`);
  79. console.time(doc._id);
  80. try {
  81. 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: ''}`;
  82. let metaInfo = await fetchMetaByTxtFromDoubao(text);
  83. console.log(metaInfo);
  84. let metaInfoJson = JSON.parse(metaInfo);
  85. let titleJson = metaInfoJson.title;
  86. let descJson = metaInfoJson.description;
  87. let title = JSON.stringify(titleJson);
  88. let desc = JSON.stringify(descJson);
  89. doc.seoTitle = title;
  90. doc.seoDescription = desc;
  91. await doc.save();
  92. } catch (e) {
  93. console.error(e.message);
  94. }
  95. console.timeEnd(doc._id);
  96. done++;
  97. duration = (Date.now() - start) / 1000;
  98. hour = (Math.floor(duration / 60 / 60)).toString().padStart(2, '0');
  99. minute = (Math.floor(duration / 60) % 60).toString().padStart(2, '0');
  100. second = (Math.floor(duration) % 60).toString().padStart(2, '0');
  101. console.log('progress: ' + Math.floor((100 * done / total)) + '% used time: ' + hour + ':' + minute + ':' + second);
  102. }
  103. }
  104. async function runVideoStoryMeta() {
  105. let done = 0;
  106. let duration = 0;
  107. let hour, minute, second;
  108. let start = Date.now();
  109. // 筛选出所有videostory
  110. let query = { enabled: true, $or: [{ seoTitle: { $exists: false } }, { seoTitle: null }] }
  111. let docs = await models.ArtVideoStory
  112. .find(query)
  113. .sort({ order: 'asc' })
  114. .select('name');
  115. let total = docs.length;
  116. console.log('total:', total);
  117. if (total <= 0) return;
  118. for (let doc of docs) {
  119. console.log(`process video-story ${doc._id}`);
  120. console.time(doc._id);
  121. try {
  122. 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: ''}`;
  123. let metaInfo = await fetchMetaByTxtFromDoubao(text);
  124. console.log(metaInfo);
  125. let metaInfoJson = JSON.parse(metaInfo);
  126. let titleJson = metaInfoJson.title;
  127. let descJson = metaInfoJson.description;
  128. let title = JSON.stringify(titleJson);
  129. let desc = JSON.stringify(descJson);
  130. doc.seoTitle = title;
  131. doc.seoDescription = desc;
  132. await doc.save();
  133. } catch (e) {
  134. console.error(e.message);
  135. }
  136. console.timeEnd(doc._id);
  137. done++;
  138. duration = (Date.now() - start) / 1000;
  139. hour = (Math.floor(duration / 60 / 60)).toString().padStart(2, '0');
  140. minute = (Math.floor(duration / 60) % 60).toString().padStart(2, '0');
  141. second = (Math.floor(duration) % 60).toString().padStart(2, '0');
  142. console.log('progress: ' + Math.floor((100 * done / total)) + '% used time: ' + hour + ':' + minute + ':' + second);
  143. }
  144. }
  145. async function runArtMeta() {
  146. let done = 0;
  147. let duration = 0;
  148. let hour, minute, second;
  149. let start = Date.now();
  150. // 筛选出所有已经ready并且还没有title的图
  151. let query = { status: { $gte: 7000 }, $or: [{ seoTitle: { $exists: false } }, { seoTitle: null }] };
  152. let docs = await models.Art.find(query).limit(1000).sort({ publishTime: 'desc' }); // 内存有限,每次跑1000个
  153. let total = docs.length;
  154. console.log('total:', total);
  155. if (total <= 0) return;
  156. for (let doc of docs) {
  157. console.log(`process ${doc._id}`);
  158. let thumbUrl = `http://color.jccytech.cn/thumbs/v2/work/320/${doc._id}.png`;
  159. if (doc.hasSpecial) {
  160. thumbUrl = `http://color.jccytech.cn/thumbs/v2/special/320/${doc._id}.png`;
  161. }
  162. console.time(doc._id);
  163. try {
  164. let metaInfo = await fetchMetaByImageFromDoubao(thumbUrl);
  165. if (!metaInfo) {
  166. console.error("something wrong, quit!");
  167. process.exit();
  168. }
  169. console.log(metaInfo);
  170. let metaInfoJson = JSON.parse(metaInfo);
  171. let titleJson = metaInfoJson.title;
  172. let seotitleJson = metaInfoJson.seotitle;
  173. let descJson = metaInfoJson.description;
  174. let copyJson = metaInfoJson.copy;
  175. let title = JSON.stringify(titleJson);
  176. let seotitle = JSON.stringify(seotitleJson);
  177. let desc = JSON.stringify(descJson);
  178. let copy = JSON.stringify(copyJson);
  179. doc.seoTitle = seotitle;
  180. doc.seoDescription = desc;
  181. doc.title = title;
  182. doc.desc = copy;
  183. await doc.save();
  184. } catch (e) {
  185. console.error(e.message);
  186. }
  187. console.timeEnd(doc._id);
  188. done++;
  189. duration = (Date.now() - start) / 1000;
  190. hour = (Math.floor(duration / 60 / 60)).toString().padStart(2, '0');
  191. minute = (Math.floor(duration / 60) % 60).toString().padStart(2, '0');
  192. second = (Math.floor(duration) % 60).toString().padStart(2, '0');
  193. console.log('progress: ' + Math.floor((100 * done / total)) + '% used time: ' + hour + ':' + minute + ':' + second);
  194. }
  195. runArtMeta();
  196. }
  197. async function run() {
  198. await runAlbumMeta();
  199. await runVideoStoryMeta();
  200. await runArtMeta();
  201. }
  202. async function test() {
  203. let metaInfo = await fetchMetaByImageFromDoubao("https://color.jccytech.cn/thumbs/v2/work/640/67a254ec4f9d65537938e5c5.png");
  204. console.log(metaInfo);
  205. let metaInfoJson = JSON.parse(metaInfo);
  206. let titleJson = metaInfoJson.title;
  207. let descJson = metaInfoJson.copy;
  208. let title = JSON.stringify(titleJson);
  209. let desc = JSON.stringify(descJson);
  210. console.log(title);
  211. console.log(desc);
  212. }
  213. module.exports = { run }
  214. if (require.main == module) {
  215. run();
  216. }