Răsfoiți Sursa

add video-story

guoziyun 1 an în urmă
părinte
comite
0fe960455c

+ 5 - 5
config/meta.js

@@ -39,11 +39,11 @@ let tagTitle = {
 }
 
 let tagDescription = {
-  zh: '涂色页标签能帮助你精准找到你想要的内容,无论是圣诞节、蝴蝶、儿童相关的,还是其他各类主题',
-  en: `Coloring pages tags help you find exactly what you’re looking for,  whether it's Christmas, butterfly, children, or beyond.`,
-  es: 'Las etiquetas de las páginas de colorear te ayudan a encontrar exactamente lo que estás buscando, ya sea temas relacionados con Navidad, mariposas, niños o cualquier otro tema más allá.',
-  pt: 'As tags das páginas de colorir ajudam você a encontrar exatamente o que está procurando, se for sobre Natal, borboletas, crianças ou qualquer coisa além disso.',
-  ja: '着色用ページのタグは、クリスマス、蝶、子供に関するものであっても、それ以外のものであっても、あなたが探しているものを正確に見つけるのに役立ちます。',
+  zh: '上百个涂色页标签,如animal,cat,dog,butterfly,flower,girl,woman等,帮你快速找到想要的填色页',
+  en: `Hundreds of coloring page tags, such as animal, cat, dog, butterfly, flower, girl, woman, etc., help you quickly find the coloring pages you want.`,
+  es: 'Cientos de etiquetas de páginas de colorear, como animal, gato, perro, mariposa, flor, chica, mujer, etc., te ayudan a encontrar rápidamente las páginas de colorear que deseas.',
+  pt: 'Centenas de tags de páginas de colorir, como animal, gato, cachorro, borboleta, flor, menina, mulher, etc., ajudam você a encontrar rapidamente as páginas de colorir que você deseja.',
+  ja: '動物、猫、犬、蝶、花、女の子、女性など、数百の塗り絵ページのタグがあり、あなたが欲しい塗り絵ページを素早く見つけることができます。',
 }
 
 let searchTitle = {

+ 2 - 0
models/index.js

@@ -7,4 +7,6 @@ module.exports.Token = mongoose.model('Token', require('./rbac/schema-token'));
 
 module.exports.Art = mongoose.model('Art', require('./schema-art'));
 module.exports.ArtAlbum = mongoose.model('Album', require('./schema-album'));
+module.exports.ArtDaily = mongoose.model('Daily', require('./schema-daily'));
+module.exports.ArtVideoStory = mongoose.model('VideoStory', require('./schema-video-story'));
 module.exports.Translate = mongoose.model('Translate', require('./schema-translate'));

+ 2 - 0
models/schema-album.js

@@ -9,6 +9,8 @@ let albumSchema = new Schema({
   enabled: { type: Boolean, default: false, index: true, desc: '启用' },
   title: { type: Schema.Types.ObjectId, ref: 'Translate', desc: '名称' }, //支持多语言, populate from coloring_ol db
   slogon: { type: Schema.Types.ObjectId, ref: 'Translate', desc: '口号' }, //支持多语言, populate from coloring_ol db
+  seoTitle: { type: String, desc: '专辑标题', searchable: true }, // 新增字段,网站title,60个字符以内, json字符串,形如: {zh: '中国', en: 'China'}
+  seoDescription: { type: String, desc: '专辑描述', searchable: true },  // 新增字段,网站seo meta  description, 160个字符以内, json字符串,形如: {zh: '中国', en: 'China'}
   order: { type: Number, default: 1000, index: true, desc: '排序', orderable: true, },
   contents: { type: [Schema.Types.ObjectId], ref: 'Art', default: [], desc: '内容' }, // 只存内容id,结合popuate
   timeCreate: { type: Date, index: true, desc: '创建时间', default: Date.now, orderable: true, },

+ 35 - 0
models/schema-video-story.js

@@ -0,0 +1,35 @@
+var Schema = require('mongoose').Schema;
+
+let videoStorySchema = new Schema({
+  name: { type: String, required: true, searchable: true, desc: '视频故事名' },
+  url: { type: String, required: true, unique: true, desc: '视频url' },
+  enabled: { type: Boolean, default: false, index: true, desc: '启用' },
+  poster: { type: Schema.Types.ObjectId, ref: 'ArtBin', require: true, desc: '海报图' },
+  title: { type: Schema.Types.ObjectId, ref: 'Translate', desc: '名称' }, //支持多语言, populate from coloring_ol db
+  slogon: { type: Schema.Types.ObjectId, ref: 'Translate', desc: '口号' }, //支持多语言, populate from coloring_ol db
+  seoTitle: { type: String, desc: '专辑标题', searchable: true }, // 新增字段,网站title,60个字符以内, json字符串,形如: {zh: '中国', en: 'China'}
+  seoDescription: { type: String, desc: '专辑描述', searchable: true },  // 新增字段,网站seo meta  description, 160个字符以内, json字符串,形如: {zh: '中国', en: 'China'}
+  order: { type: Number, default: 1000, index: true, desc: '排序', orderable: true, },
+  contents: { type: [Schema.Types.ObjectId], ref: 'Art', default: [], desc: '内容' }, // 只存内容id,结合popuate
+  user: { type: Schema.Types.ObjectId, ref: 'User', required: true, index: true, desc: '创建者' },
+  timeCreate: { type: Date, index: true, desc: '创建时间', default: Date.now, orderable: true, },
+  timeLastModify: { type: Date, index: true, desc: '最后修改时间', default: Date.now, orderable: true, },
+}, {
+  strict: true,
+  toJSON: {
+    virtuals: true,
+    transform: storyTransform,
+  },
+  toObject: {
+    virtuals: true,
+    transform: storyTransform,
+  },
+
+});
+
+function storyTransform(doc, ret) {
+  ret.poster = `/res/coloring/vs-poster/480/${doc._id}.png`;
+  ret.size = doc.contents ? doc.contents.length : 0;
+}
+
+module.exports = videoStorySchema;

+ 92 - 29
service/cron-jobs/fetch-meta.js

@@ -1,30 +1,6 @@
 const models = require('../../models');
 const fetch = require('node-fetch');
 
-/**
- * 
-curl --location 'https://ark.cn-beijing.volces.com/api/v3/chat/completions' \
---header 'Authorization: Bearer fb8942c2-fe94-4092-80fc-233e252f7090' \
---header 'Content-Type: application/json' \
---data '{
-        "model": "ep-20250206115552-7qg5c",
-        "messages": [
-        {"role": "user", "content": [
-            {
-                "type":"text",
-                "text": "根据图片生成标题(title, 60个字符以内)、简要描述(description, 用于网站SEO的meta description,160个字符以内,尽量包含Coloring Page关键字)、文案描述(copy, 200字左右,尽量包含Coloring Page 关键字)。 以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": "https://color.jccytech.cn/thumbs/v2/work/320/67a241674f9d65537938e36d.png"
-                }
-            }
-        ]}
-    ]
-}'
-*/
-
 let apiKey = require('process').env.ARK_API_KEY;
 const url = "https://ark.cn-beijing.volces.com/api/v3/chat/completions";
 let headers = {
@@ -33,10 +9,11 @@ let headers = {
 }
 
 /**
+ * 豆包视觉API
  * 从豆包获取图片的标题和文案描述
  * @param {*} imageUrl 
  */
-async function fetchMetaFromDoubao(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  便宜点,相应速度快
@@ -58,6 +35,8 @@ async function fetchMetaFromDoubao(imageUrl) {
     ]
   }
 
+  console.log(data);
+
   const jsonData = JSON.stringify(data);
 
   const response = await fetch(url, { method: 'POST', headers, body: jsonData });
@@ -69,9 +48,89 @@ async function fetchMetaFromDoubao(imageUrl) {
   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);
 
-async function run() {
+  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 runArtMeta() {
   let done = 0;
   let duration = 0;
   let hour, minute, second;
@@ -98,7 +157,7 @@ async function run() {
     console.time(doc._id);
 
     try {
-      let metaInfo = await fetchMetaFromDoubao(thumbUrl);
+      let metaInfo = await fetchMetaByImageFromDoubao(thumbUrl);
       console.log(metaInfo);
       let metaInfoJson = JSON.parse(metaInfo);
       let titleJson = metaInfoJson.title;
@@ -129,14 +188,18 @@ async function run() {
 
   }
 
-  run();
+  runArtMeta();
 
 }
 
+async function run() {
+  await runAlbumMeta();
+  await runArtMeta();
+}
 
 
 async function test() {
-  let metaInfo = await fetchMetaFromDoubao("https://color.jccytech.cn/thumbs/v2/work/640/67a254ec4f9d65537938e5c5.png");
+  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;

+ 1 - 0
sync/sync-service.js

@@ -41,6 +41,7 @@ async function run() {
           && eventDoc.tb != 'daily-arts'
           && eventDoc.tb != 'users'
           && eventDoc.tb != 'albums'
+          && eventDoc.tb != 'videostories'
           && eventDoc.tb != 'translates') {
           continue;
         }